Commit Graph

18 Commits

Author SHA1 Message Date
johannes d8619c4ecb fix: remove duplicate CORS headers — disable Spring CORS, delegate to NGINX
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 15:20:26 -04:00
johannes 746b598f4d fix(feed): widen source_ref from VARCHAR(100) to TEXT
CD - Develop / build-and-deploy (push) Failing after 52s
OTX pulse names exceed 100 characters, causing a column-too-long error on
IOC feed upsert. TEXT has no length limit and matches the description column.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 11:49:28 -04:00
johannes 1a47e2dea9 feat(aria): IOC reported_at, threat stats 24h window, filter sidebar, posture UX
CD - Develop / build-and-deploy (push) Failing after 11m51s
- Add reported_at field to threat_ioc (V7 migration) — tracks when the source
  originally published the IOC, distinct from addedAt (ingestion time); wired
  into OTX (created field), MalwareBazaar and ThreatFox (first_seen field),
  and the manual IOC creation endpoint
- Fix threat event stats to use a 24h time window for critical/high counts
  (countBySeverityAndDetectedAtAfter) instead of all-time totals
- Rebuild IOC Management with canonical filter sidebar: collapsible left sidebar
  with per-type toggle pills, active filter chips with individual removal
- DevicePosture: collapse institutions and filter sidebar by default

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 20:45:19 -04:00
johannes 571d88db4d fix(aria): IOC Feeds — restore app, fix API shapes, canonical UI
CD - Develop / build-and-deploy (push) Failing after 10m58s
Root cause: IocFeeds.svelte called feeds.find() on a Map (backend returned
Map<String,Object> not List) → reactive statement crash → entire Svelte tree
broken, all sidebar nav dead.

Backend:
- /feeds/status now returns List<Map> with feedName/configured/enabled/
  lastRunAt/lastRunStatus/lastIocsAdded/lastIocsUpdated fields
- /feeds/runs now returns full Page<IocFeedRun> (not stripped .getContent())
- IocFeedService.isConfigured(feedName) added for per-feed key check
- Feed order fixed to [OTX, MALWARE_BAZAAR, THREAT_FOX] (deterministic)

Frontend:
- loadFeeds: Array.isArray guard prevents crash if shape is ever wrong
- loadRuns: safe fallback for both Page and plain-array responses
- api.ts refreshFeed URL fixed: /feeds/refresh/{name} not /feeds/{name}/refresh
- IocFeedStatus type: added lastIocsUpdated
- IocFeeds.svelte: Refresh All moved from header to toolbar; all buttons
  use canonical btn/btn-primary/btn-secondary/btn-sm classes; pollInterval
  now calls pollStatus (status-only poll) not full refresh trigger

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 22:41:21 -04:00
johannes 583aff9c2d fix(aria): feed clients — @Param binding, auth keys, thread error logging
CD - Develop / build-and-deploy (push) Failing after 12m28s
- IocFeedRunRepository: add @Param("feedName") to countRunning JPQL query
  (missing binding silently crashed background threads)
- IocFeedController: uncaught exception handler on feed threads so failures
  appear in logs instead of disappearing silently
- MalwareBazaarFeedClient/ThreatFoxFeedClient: abuse.ch now requires API
  key (auth.abuse.ch); add Auth-Key header + isConfigured() skip guard
- application.properties: add aria.feed.malware-bazaar.api-key and
  aria.feed.threat-fox.api-key properties

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:59:50 -04:00
johannes bdc2504635 feat(aria): Phase 5 — IOC feed refresh from OTX, MalwareBazaar, ThreatFox
CD - Develop / build-and-deploy (push) Failing after 52s
Adds automated daily IOC ingestion from three external threat feeds:
- AlienVault OTX: subscribed pulses + ATM-keyword searches (requires API key)
- MalwareBazaar (abuse.ch): ATM-tagged malware samples, no key required
- ThreatFox (abuse.ch): ATM malware family IOCs, no key required

Backend:
- IocFeedRun entity + V6 Flyway migration (ioc_feed_run table)
- OtxFeedClient / MalwareBazaarFeedClient / ThreatFoxFeedClient
- IocFeedService: upsert dedup + @Scheduled daily 03:00 refresh
- IocFeedController: GET /status, GET /runs, POST /refresh, POST /refresh/{feed}
- ThreatIoc.IocSource enum extended: OTX, MALWARE_BAZAAR, THREAT_FOX

Frontend:
- IocFeeds.svelte: feed status cards, run history table, manual refresh
- App.svelte: IOC Feeds nav item (📡), version string removed from sidebar
- api.ts: getFeedStatus, getFeedRuns, refreshAllFeeds, refreshFeed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 21:01:09 -04:00
johannes 1663ec8c03 security(aria): restrict all endpoints to BCOS_ADMIN only
CD - Production / build-and-deploy (push) Failing after 55s
CD - Develop / build-and-deploy (push) Failing after 11m52s
ARIA is an internal security intelligence tool — MSP_ADMIN users
should not have access. Replaced hasAnyRole('MSP_ADMIN','BCOS_ADMIN')
with hasRole('BCOS_ADMIN') across all four controllers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 19:05:14 -04:00
johannes 23a36cd824 fix(aria): enable Spring Data page DTO serialization for consistent pagination JSON
CD - Develop / build-and-deploy (push) Failing after 11m57s
CD - Production / build-and-deploy (push) Failing after 13m10s
Without @EnableSpringDataWebSupport(VIA_DTO) the API returned totalElements
at the top level. Frontend expects {content, page: {totalElements, totalPages,
number}} — the DTO format. Fixes Load Failed on all paginated pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:00:27 -04:00
johannes b42e1548bc fix(aria): drop NOT NULL on device_id after PK migration
CD - Develop / build-and-deploy (push) Failing after 12m32s
V4 dropped the PK but PostgreSQL retained the implicit NOT NULL.
V5 makes device_id nullable so agents without a numeric deviceId
can persist posture reports.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:44:51 -04:00
johannes 1d6ccff529 fix(aria): add BIGSERIAL PK to device_security_posture, fix JPA persist error
CD - Develop / build-and-deploy (push) Failing after 15m8s
V4 migration drops the device_id primary key and adds an auto-generated
id BIGSERIAL as the PK. device_id becomes a unique nullable column.
Agents that don't send a numeric deviceId can now save posture reports.
Controller GET /{deviceId} now uses findByDeviceId instead of findById.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:42:16 -04:00
johannes 726ccb4ecf fix(aria): correct V3 column names and switch filters to select dropdowns
CD - Develop / build-and-deploy (push) Failing after 13m2s
V3 migration had wrong column names for threat_ioc (source, confidence)
and precursor_sequence_alert (auto_response_taken). Fixed column names
and added phases_detected TEXT[] conversion. PrecursorAlerts and
DevicePosture filter sidebar now uses <select> dropdowns per template
pattern (filter-sidebar structure kept; pill buttons removed).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:29:22 -04:00
johannes cf275d87d1 fix(aria): convert all PostgreSQL named enum columns to VARCHAR
CD - Develop / build-and-deploy (push) Failing after 11m27s
All named enum types (threat_severity, attack_phase, event_type, ioc_type,
ioc_source, confidence_level, sequence_type, auto_response_action,
os_eol_status) have been converted to VARCHAR via V3 migration. Hibernate
@Enumerated(EnumType.STRING) binds values as character varying which
PostgreSQL rejects without explicit casts on both INSERT and SELECT.

Reverts the native @Query workarounds added in the previous two commits —
simple derived query methods now work correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:05:56 -04:00
johannes 89a088c4b3 fix(aria): fix os_eol_status enum cast — use native SQL throughout
CD - Develop / build-and-deploy (push) Failing after 14m42s
PostgreSQL named enum types require explicit casts that Hibernate's
derived query methods don't generate. Replace countByOsEolStatus and
findByOsEolStatusOrderByPostureScoreAsc with native @Query variants that
CAST(:status AS os_eol_status). Same pattern already applied to severity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:57:41 -04:00
johannes 1e7625db8b fix(aria): fix stats 500 — use native SQL cast for threat_severity enum
CD - Develop / build-and-deploy (push) Failing after 52s
PostgreSQL named enum types don't accept implicit varchar casts, causing
the derived JPA query findBySeverityOrderByDetectedAtDesc to fail with
'operator does not exist: threat_severity = character varying'.

Replace the count calls in getStats() with a native query that explicitly
casts: CAST(:severity AS threat_severity). Also remove the unused since24h
and since7d variables.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:36:53 -04:00
johannes 68cee9bd90 feat(aria): Phase 3 — device security posture tracking
CD - Develop / build-and-deploy (push) Failing after 14m13s
Adds per-device security posture reports and compliance dashboard. Agents POST
posture data (disk encryption, audit policy, image hashes, OS version/EOL) to
POST /api/aria/posture/report (service-secret authenticated). DevicePostureService
upserts the record and calculates a 0–100 posture score using a penalty model:
disk encryption off −25, audit policy off −20, OS EOL −20, image hash drift −20,
whitelist off −10, stale check −5/day (capped at −15). REST API adds GET /posture
(filterable by EOL status and score band) and GET /posture/{deviceId}. Stats
endpoint gains lowPostureCount (<50) and eolDeviceCount. Dashboard reorganised
into Threat Activity and Compliance rows. Device Posture SPA view enables the
previously-disabled nav item with score bar, EOL badges, and full detail panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 14:38:11 -04:00
johannes aa9f8a10ef feat(aria): Phase 2 — precursor sequence detection engine
CD - Develop / build-and-deploy (push) Failing after 11m45s
Adds multi-phase attack sequence detection to ARIA. After each event is
ingested, SequenceDetectionService queries events within a 15-minute window
for the same device and fires a PrecursorSequenceAlert when jackpotting
(PHYSICAL_ACCESS + malware phase) or skimming (PHYSICAL_ACCESS + card reader
tamper/USB) patterns are detected. Confidence is scored 0–1 based on phases
and severity. Detected sequences are published to hiveops.aria.sequences Kafka
topic. Adds REST API (GET /sequences, GET /{id}/events, POST /{id}/resolve) and
enables the Precursor Alerts view in the SPA with filter sidebar, confidence
bar, and event detail slide panel.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 14:25:35 -04:00
johannes fe9a252dcd feat(aria): add Svelte SPA frontend and threat events API
CD - Develop / build-and-deploy (push) Failing after 54s
- frontend/: full aria.bcos.dev SPA (port 5187) with Dashboard, Threat Events,
  and IOC Management views; Phase 2-3 nav items disabled as placeholders
- ThreatEventController: GET /api/aria/events (paginated, severity/device filter)
  and GET /api/aria/stats (total/critical/high/activeIoc counts)
- Deployed to bcos.dev: hiveiq-aria-frontend container + aria.bcos.dev NPM proxy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 10:00:07 -04:00
johannes e5f25f1c2c feat: Phase 1 — ARIA ATM threat monitoring service
CD - Develop / build-and-deploy (push) Failing after 11m31s
- Spring Boot service on port 8095, database hiveiq_aria
- Flyway schema: threat_ioc, threat_event, precursor_sequence_alert, device_security_posture
- FBI FLASH-20260219-001 IOC seed data (executables, MD5s, directories, registry keys, services)
- ThreatEventClassifier — maps 20+ signal keys to severity + attack phase + IOC match
- ThreatEventIngestionService — persists events, publishes HIGH/CRITICAL to Kafka
- Kafka producer: topic hiveops.aria.threats (AriaThreatEvent payload)
- ThreatIocController — CRUD IOC management (MSP_ADMIN/BCOS_ADMIN)
- ThreatEventIngestionController — agent-facing ingest endpoint with service secret auth
- CI/CD: cd-develop.yml + cd-main.yml
2026-06-14 09:30:46 -04:00