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
- OS: macOS 26 (reproduction), observed on staging (main)
- Version: pyoaev 3.260821.0 (Nmap injector) and 3.260923.0 (SentinelOne collector, latest).
pyoaev/signatures/ has no changes between these two versions.
- Other environment details: reproduced locally with Mimikyu (fake OpenAEV) + ofapi (fake SentinelOne API)
Reproducible steps
- Run the Nmap injector against an OpenAEV platform (or Mimikyu) and launch a scan inject on an asset with Detection/Prevention expectations.
- 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.
- 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
Description
SignatureManager.build_payloadsends the field names ofExecutionSignatureas signature types:start_time,end_time,source_ipv4,source_ipv6,target_ipv4,target_ipv6,target_hostname. The canonical names arestart_date,end_date,source_ipv4_address,source_ipv6_address,target_ipv4_address,target_ipv6_addressandtarget_hostname_address.The platform stores these strings as is. Collectors then validate them against
SignatureTypes, so buildingDetectionExpectation/PreventionExpectationfails:expectations_models_for_sourcebuilds 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 usesSignatureManagerbrings the bug back (Nmap confirmed; netexec, nuclei and email also useSignatureManager).Extra signatures go through unchanged too. Nmap sends
ports_discovered/services_discovered, which aren't inSignatureTypesand have list values.Environment
pyoaev/signatures/has no changes between these two versions.Reproducible steps
execution_output_structured). Signature types arestart_time,end_time,source_ipv4,source_ipv6,target_ipv4,ports_discovered,services_discovered.Error fetching expectations: N validation errors for PreventionExpectation ... input_value='start_time'followed byNo expectations found to process.Minimal check without a platform:
Expected output
The signature payload only uses
SignatureTypesvalues (start_date,end_date,source_ipv4_address,target_ipv4_address, ...). Collectors parse pending expectations and match them.Actual output
The payload uses the
ExecutionSignaturefield 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): mapExecutionSignaturefields to canonicalSignatureTypesbefore buildingSignatureValues, and reject/skip keys that aren't validSignatureTypes(e.g.partial_results, Nmap'sports_discovered/services_discovered). Add a test that checks the real payload types. The Nmap injector tests mockSignatureManagerentirely, 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 theNonesignatures fix for collectors#490).==3.260821.0), netexec, nuclei and email