Problem/Motivation

MCP Sentinel already evaluates anomaly rules by operation, time window, threshold, and debounce interval. It also detects thresholded denied-access storms. The local rule set does not yet cover activity outside an approved operating-hours schedule or complete and near-complete bulk reads across a governed collection.

Without these signals, an operator can detect repeated denials but cannot consistently distinguish unusual access timing or broad data collection from normal governed activity.

Proposed resolution

Extend the existing anomaly engine with configurable off-hours and complete bulk-read signals. Reuse the current rule evaluation, threshold, window, debounce, audit, and notification seams rather than introducing a second anomaly subsystem.

Preserve existing denied-access storm behavior as regression coverage.

Acceptance criteria

  • Administrators can define an operating-hours schedule and timezone for governed activity.
  • Governed activity outside the configured schedule produces a stable off-hours anomaly signal.
  • Complete or near-complete reads of a governed collection produce a stable bulk-read anomaly signal.
  • Bulk-read detection covers every supported local governed read channel that can enumerate a collection.
  • Threshold, time-window, and debounce behavior remains configurable and deterministic.
  • Existing thresholded denied-access storm detection remains covered by regression tests.
  • Audit evidence identifies the signal, governed actor context, target scope, rule version, window, threshold, and outcome without exposing credentials or sensitive payload content.
  • Tests cover allowed-hours activity, off-hours activity, partial reads, complete reads, repeated and debounced detection, and disabled rules.

Scope boundary

This issue owns local off-hours and complete bulk-read signals. Hosted tenant/principal correlation and cross-system anomaly aggregation are separate work.

API changes

The existing anomaly rule contract gains the configuration needed for operating-hours and complete bulk-read signals.

Data model changes

Any rule-schema change must remain backward compatible with existing anomaly rules.

Comments

jmcerda created an issue. See original summary.

  • jmcerda committed 4a0c7e7b on feature/3616612-anomaly-off-hours-bulk
    Issue #3616612: restack onto 1.x after dashboard evidence (#145).
    
    Keep...

  • jmcerda committed b89ab841 on feature/3616612-anomaly-off-hours-bulk
    Issue #3616612: restack onto 1.x after DLP classification.
    
    Resolve...

  • jmcerda committed 4a0c7e7b on 1.x
    Issue #3616612: restack onto 1.x after dashboard evidence (#145).
    
    Keep...

  • jmcerda committed b89ab841 on 1.x
    Issue #3616612: restack onto 1.x after DLP classification.
    
    Resolve...

  • jmcerda committed b43ef4f3 on 1.x
    Issue #3616612: omit default count signal so rules round-trip
    

  • jmcerda committed b9ff6f39 on 1.x
    Issue #3616612: add off-hours and bulk-read anomaly signals
    
    Operating-...

  • jmcerda committed 4a0c7e7b on feature/3616536-cite-digest
    Issue #3616612: restack onto 1.x after dashboard evidence (#145).
    
    Keep...

  • jmcerda committed b89ab841 on feature/3616536-cite-digest
    Issue #3616612: restack onto 1.x after DLP classification.
    
    Resolve...

  • jmcerda committed b43ef4f3 on feature/3616536-cite-digest
    Issue #3616612: omit default count signal so rules round-trip
    

  • jmcerda committed b9ff6f39 on feature/3616536-cite-digest
    Issue #3616612: add off-hours and bulk-read anomaly signals
    
    Operating-...

  • jmcerda committed 4a0c7e7b on feature/3616536-portable-policy-bundles
    Issue #3616612: restack onto 1.x after dashboard evidence (#145).
    
    Keep...

  • jmcerda committed b89ab841 on feature/3616536-portable-policy-bundles
    Issue #3616612: restack onto 1.x after DLP classification.
    
    Resolve...

  • jmcerda committed b43ef4f3 on feature/3616536-portable-policy-bundles
    Issue #3616612: omit default count signal so rules round-trip
    

  • jmcerda committed b9ff6f39 on feature/3616536-portable-policy-bundles
    Issue #3616612: add off-hours and bulk-read anomaly signals
    
    Operating-...

  • 6706613a committed on fix/3616612-anomaly-evidence
    Issue #3616612: evict GraphQL results when Log reads is enabled...

  • 31f3439a committed on fix/3616612-anomaly-evidence
    Issue #3616612: keep GraphQL entity_read audit off the operation cache...

  • jmcerda committed 15831f98 on 1.x
    Issue #3616612: keep #3616538 unreleased notes when merging 1.x
    

  • jmcerda committed 4713b993 on 1.x
    Issue #3616612: exclude GraphQL write echoes from entity_read evidence...

  • jmcerda committed 64819fd8 on 1.x
    Issue #3616612: keep GraphQL entity_read off the operation cache
    
    Evict...

  • jmcerda committed 8bef4898 on 1.x
    Issue #3616612: emit bulk-read rows and bounded alert evidence
    
    Live...
jmcerda’s picture

Version: 2.3.0 » 2.13.0
Status: Active » Fixed

Released in 2.13.0 (commit cdd53eb on 1.x). Marking Fixed.

A fired rule now writes a bounded anomaly_alert audit row, and the same fields travel on the webhook. Governed JSON:API GET/HEAD documents and GraphQL field resolutions emit one entity_read per distinct entity when Log reads is on, so bulk_read can see those channels. Hosted tenant correlation remains out of scope.

https://www.drupal.org/project/mcp_sentinel/releases/2.13.0

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.