Skip to content

oat.consume() raises ValidationError on tenants with non-endpoint/email OAT events (5 of 7 OatEntityType values not modeled) #60

Description

@fernando-karl

Summary

OatEvent.detail is typed as Union[EndpointActivity, EmailActivity], covering only 2 of the 7 values declared in OatEntityType. Any OAT event whose entity type is cloudtrail, messaging, network,
ics, or container fails pydantic validation, which raises ValidationError from oat.consume() (and oat.list()) and aborts pagination mid-stream. The SDK currently has no fallback path for these
events.

Affected version

pytmv1 == 0.11.0 (also present on main, latest commit at the time of writing).

Root cause

  • src/pytmv1/model/enum.py:157-164 — OatEntityType has 7 members:

    class OatEntityType(str, Enum):
        ENDPOINT   = "endpoint"
        MAILBOX    = "mailbox"
        CLOUDTRAIL = "cloudtrail"
        MESSAGING  = "messaging"
        NETWORK    = "network"
        ICS        = "ics"
        CONTAINER  = "container"
  • src/pytmv1/model/common.py:625 — OatEvent.detail only models 2 of those 7:

    class OatEvent(BaseConsumable):
        ...
        entity_type: OatEntityType                                                                                                                                                 
        ...
        detail: Union[EndpointActivity, EmailActivity]
  • EndpointActivity requires endpoint_guid (endpointGuid); EmailActivity requires msg_uuid (msgUuid). For an event with entity_type ∈ {cloudtrail, messaging, network, ics, container}, neither
    branch of the union validates, so pydantic raises ValidationError and the consumer never sees the event — nor any later events on that page or subsequent pages.

Observed behavior

Reproduced against a production tenant via client.oat.consume(consumer, start_time=..., end_time=..., top=50):

  • Pages 1–2: ~101 events consumed successfully.
  • Page 3: parse fails on 2 of 50 events whose detail payload looks like {"act": ["log"], ...} (no endpointGuid, no msgUuid) — consistent with a non-endpoint/non-email entity type. The exception
    aborts the entire consume() call.

Prevalence in our sample was ~4% (2/50 in a single page), so any tenant running cloudtrail / network / container detection rules will hit this in practice. The bug is not a transient HTTP / timeout issue —
increasing read_timeout or shrinking page_size does not help, since the failure is deterministic on payload shape.

Impact

  • oat.consume() and oat.list() are effectively unusable on real tenants that use OAT detections beyond endpoint/email.
  • There is no in-SDK workaround: consume() parses internally, so the caller cannot intercept the raw response before validation. Falling back to a manual HTTP client defeats the purpose of using the SDK
    for OAT.
  • For comparison, alert.consume() is unaffected because SaeAlert / TiAlert cover the full set of providers returned by the Workbench API.

Proposed fix

Two options, in increasing scope:

  1. Minimal — add a permissive fallback. Change

    detail: Union[EndpointActivity, EmailActivity]

    to

    detail: Union[EndpointActivity, EmailActivity, Dict[str, Any]]

    so that endpoint and email events stay strongly typed while non-modeled types are surfaced as a raw dict and the consume loop keeps making forward progress. ~1 line, fully backward-compatible.

  2. Complete — model the remaining 5 entity types. Add CloudTrailActivity, MessagingActivity, NetworkActivity, IcsActivity, ContainerActivity (or whatever names match the public API doc) and
    discriminate on entity_type. Larger PR, requires the API schema for each, but gives users typed access to every event.

Happy to send a PR for option 1 if that path is acceptable; option 2 would need guidance on the expected schemas (or pointers to the API reference for these entity types).

Temporary workaround for affected users

If the API supports server-side filtering on entityType, restricting the query to endpoint and mailbox keeps consume() working at the cost of dropping the other 5 types entirely. This is a stop-gap,
not a fix — the dropped events are exactly the ones some tenants care most about (cloudtrail, network).

Environment

  • pytmv1 == 0.11.0
  • Python 3.10
  • Reproduced against a production Vision One tenant; cannot share payloads publicly, but happy to share a redacted example privately if useful.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions