Skip to content

fix(signatures): SignatureManager sends non-canonical signature types (start_time, source_ipv4, ...) that break collectors #352

Description

Description

SignatureManager.build_payload sends the field names of ExecutionSignature as signature types: start_time, end_time, source_ipv4, source_ipv6, target_ipv4, target_ipv6, target_hostname. The canonical names are start_date, end_date, source_ipv4_address, source_ipv6_address, target_ipv4_address, target_ipv6_address and target_hostname_address.

# pyoaev/signatures/signature_manager.py - build_payload
signature_data = signature.model_dump(exclude_none=True)
...
SignatureValue(signature_type=key, signature_value=value)
    for key, value in signature_data.items()

The platform stores these strings as is. Collectors then validate them against SignatureTypes, so building DetectionExpectation / PreventionExpectation fails:

5 validation errors for PreventionExpectation
inject_expectation_signatures.0.type
  Input should be 'process_name', ... [type=enum, input_value='start_time', input_type=str]

expectations_models_for_source builds the whole batch at once, so one bad expectation fails the fetch. The collector then receives no expectations on every cycle, and valid expectations from other injects (e.g. EICAR on an agent) never get matched and expire as FAILED.

Same symptom as OpenAEV-Platform/openaev#7114. That issue says "no code path emits the legacy names anymore", but SignatureManager (#206, June 2026) does. The migration in OpenAEV-Platform/openaev#7115 only repaired existing rows, so each new run of an injector that uses SignatureManager brings the bug back (Nmap confirmed; netexec, nuclei and email also use SignatureManager).

Extra signatures go through unchanged too. Nmap sends ports_discovered / services_discovered, which aren't in SignatureTypes and have list values.

Environment

  1. OS: macOS 26 (reproduction), observed on staging (main)
  2. Version: pyoaev 3.260821.0 (Nmap injector) and 3.260923.0 (SentinelOne collector, latest). pyoaev/signatures/ has no changes between these two versions.
  3. Other environment details: reproduced locally with Mimikyu (fake OpenAEV) + ofapi (fake SentinelOne API)

Reproducible steps

  1. Run the Nmap injector against an OpenAEV platform (or Mimikyu) and launch a scan inject on an asset with Detection/Prevention expectations.
  2. Look at the signature callback (execution_output_structured). Signature types are start_time, end_time, source_ipv4, source_ipv6, target_ipv4, ports_discovered, services_discovered.
  3. Run any collector (e.g. SentinelOne) while those expectations are pending. Each cycle logs Error fetching expectations: N validation errors for PreventionExpectation ... input_value='start_time' followed by No expectations found to process.

Minimal check without a platform:

import os
from pyoaev.signatures.signature_manager import SignatureManager
from pyoaev.signatures.models import NetworkInjectorConfig

os.environ["CONTAINER_IP"] = "10.0.0.5"
sig = SignatureManager(client=None).build_execution_signatures(
    NetworkInjectorConfig(target_ipv4="192.0.2.11")
)
print(list(sig.model_dump(exclude_none=True)))
# ['start_time', 'source_ipv4', 'source_ipv6', 'target_ipv4']  -> sent as signature types

Expected output

The signature payload only uses SignatureTypes values (start_date, end_date, source_ipv4_address, target_ipv4_address, ...). Collectors parse pending expectations and match them.

Actual output

The payload uses the ExecutionSignature field names as types. Every collector that fetches those expectations fails validation on each cycle, and all of its expectations end up FAILED.

Additional information

Suggested fix:

  • build_payload (pyoaev/signatures/signature_manager.py): map ExecutionSignature fields to canonical SignatureTypes before building SignatureValues, and reject/skip keys that aren't valid SignatureTypes (e.g. partial_results, Nmap's ports_discovered / services_discovered). Add a test that checks the real payload types. The Nmap injector tests mock SignatureManager entirely, so this wasn't caught.
  • expectations_models_for_source (pyoaev/apis/inject_expectation/inject_expectation.py): build expectations one by one and log/skip the invalid ones, so one bad expectation no longer blocks the whole batch (same idea as the None signatures fix for collectors#490).
  • Follow-ups:
    • release pyoaev and bump the pin in nmap (currently ==3.260821.0), netexec, nuclei and email
    • on the platform: normalize or reject unknown signature types at ingestion, and run a new repair migration for rows written since #7115

Activity

  1. guzmud commented on Oct 6, 2026

    @guzmud
    Member

    Nota bene: some elements were added to SignatureTypes as legacy support during the rewrite of microsoft defender. AFAIK, we haven't analyzed which injectors and which collectors are using those "legacy" (I guess) elements compared to which ones are using the more recent ones.

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

Metadata

Metadata

Labels

bugType: something isn't working (fix:).

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions