Skip to content

ML-assisted severity scoring (learned, context-aware risk_score) #1023

Description

@sercanokur

Is your feature request related to a problem? Please describe.

Malcolm's current severity/risk scoring (logstash/pipelines/enrichment/23_severity.conf

  • logstash/maps/malcolm_severity.yaml) works by attaching string tags to
    event.severity_tags and then summing static integer weights per tag,
    capped at 100, in the ruby_calculate_final_severity_score block.

This additive, static approach has a few inherent limitations:

  1. No interaction effects. The score is a flat sum, so high-signal
    combinations (e.g. Outbound traffic + High entropy domain +
    Service on non-standard port + executable file transfer — a classic C2
    pattern) score the same as the sum of their parts, even though together
    they are far more suspicious.
  2. Global, context-free thresholds. TOTAL_MEGABYTES_SEVERITY_THRESHOLD,
    FREQ_SEVERITY_THRESHOLD, etc. are the same for every host. A 1 GB transfer
    is normal from a backup server but alarming from an OT PLC. Asset/segment
    context (already available via NetBox enrichment) is not factored in.
  3. No analyst feedback loop. False-positive/true-positive dispositions made
    by analysts do not influence future scoring; weights are tuned by hand.
  4. Rarity-blind. A tag combination never seen before in the environment
    scores identically to one seen thousands of times per day.

Describe the solution you'd like

Keep the existing tag-generation logic untouched (it is effectively a rich,
interpretable feature-extraction layer) and augment/replace only the final
scoring step
with an optional, opt-in ML model. event.severity_tags
(multi-hot) plus a few numeric fields (freq scores, network.bytes,
destination.port, network.direction, asset role) become the feature vector.

Proposed phased rollout:

  • Phase 1 — unsupervised, zero-label cold start. A rarity/anomaly model
    (e.g. Isolation Forest) over tag combinations that raises severity for
    unusual combinations, while the existing static sum remains a floor so there
    is never a regression. Works fully offline / air-gapped with no labels.
  • Phase 2 — supervised, calibrated model. Once analyst dispositions accrue
    (a simple verdict field convention) a calibrated gradient-boosted model
    (e.g. LightGBM exported to ONNX) outputs a 0–100 risk probability, with
    per-tag contribution output (SHAP) so analysts can see why a score was
    assigned. Weak supervision (YARA/ClamAV signatures, Intelligence hits,
    high-severity Suricata alerts as positives) can bootstrap before labels exist.

Suggested integration, mirroring existing patterns to minimize disruption:

  • A new lightweight scoring microservice modeled on freq-server
    (FastAPI + ONNX runtime, model bundled into the image — no external calls).
  • A new Logstash ruby filter modeled on logstash/ruby/freq_lookup.rb
    (HTTP call + LRU cache) invoked at the end of 23_severity.conf.
  • A single service entry in docker-compose.yml / docker-compose-dev.yml
    and a Dockerfile under Dockerfiles/.
  • Gated behind a new env var (e.g. LOGSTASH_SEVERITY_ML, default false),
    so existing behavior is preserved exactly when disabled.

Describe alternatives you've considered

  • Manual weight tuning only — does not address interactions, context, or
    feedback; current state.
  • A separate standalone anomaly-detection pipeline — higher value for some
    use cases (beaconing, ICS baselining) but a larger surface area and does not
    directly improve the existing risk_score analysts already rely on.
  • Heavy deep-learning models — rejected due to Malcolm's frequent
    air-gapped, laptop-to-server deployment footprint; lightweight CPU models
    (Isolation Forest / GBM via ONNX) fit far better.

Additional context

Design goals specific to Malcolm: must run fully offline/air-gapped (model
bundled, no cloud), be lightweight enough for laptop-class deployments, remain
explainable for SOC analysts (preserve the existing severity_tags UX and emit
per-tag contributions), and be fully backward-compatible (opt-in).

Question for the maintainers: Does this direction align with the project's
roadmap and design philosophy for severity scoring? Are there constraints or
preferences (e.g. on adding a new container, model formats, or how analyst
feedback should be captured) we should design around?

If the maintainers think this is a good fit, we are interested in working on it
— starting with a Phase 1 proof-of-concept (the rarity-based scorer microservice

  • ruby filter + opt-in integration) for review before going further.

Metadata

Metadata

Assignees

Labels

AIRelated to Malcolm's use of AI (or AI's use of Malcolm)enhancementNew feature or request

Fields

No fields configured for Feature.

Projects

Status
In Progress (external)

Relationships

None yet

Development

No branches or pull requests

Issue actions