[client-python] feat(signature): first US for ExpectationSignature (#206)#257
Open
Kakudou wants to merge 13 commits into
Open
[client-python] feat(signature): first US for ExpectationSignature (#206)#257Kakudou wants to merge 13 commits into
Kakudou wants to merge 13 commits into
Conversation
Member
Author
|
Initial message edited to reflect the new behavior based on review/usages. Also as for the NetworkInjectorConfig, we can use a builder to quickly create them from a list of targets (from build_network_configs(["10.0.0.1", "2001:db8::1", "web.example.com"]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
That's the first US to introduce the SignatureExpectation for the injectors.
Proposed changes
SignatureManager
Unified signature lifecycle for OpenAEV injectors: compile pre-execution signatures, merge post-execution results, and ship structured output to the backend.
Architecture
flowchart LR subgraph pyoaev/signatures SM[SignatureManager] M[models.py] CFG["InjectorConfig\n(Network / Cloud / External)"] end subgraph pyoaev/apis API[SignatureApiManager] end subgraph Backend CB["/api/injects/{id}/callback"] end CFG -->|typed input| SM SM -->|compile_pre/post| M SM -->|send_signatures| API API -->|callback\nretry + chunk| CBInjector configs (
models.py) are the typed contract: one config = one signature row.SignatureManager owns the domain logic (compile, merge, resolve IP).
SignatureApiManager owns the transport (validation, chunking, retry).
Quick Start
Injector configs
The category is encoded in the config type. Pass a single config for a single-target inject,
or a homogeneous list for a multi-target inject. Mixing config types in a single call is rejected.
NetworkInjectorConfigtarget_ipv4/target_ipv6/target_hostnameCloudInjectorConfigcloud_provider,cloud_account_id,cloud_regiontarget_serviceExternalInjectorConfigquerytarget_ipv4,target_hostnameInjectorConfigis the union type:NetworkInjectorConfig | CloudInjectorConfig | ExternalInjectorConfig.SignatureManageraddsstart_timeautomatically (plussource_ipv4/source_ipv6for network).Network
####### Network builder
build_network_configs(targets)turns a heterogeneous list of strings, dicts, or already-typedNetworkInjectorConfiginto a clean list of typed configs. Strings are auto-classified intoIPv4 / IPv6 / hostname via the stdlib
ipaddressmodule. Each input is treated as one distinctasset — a target never mixes identities.
Cloud
External
Compiled output shapes
compile_pre_execution_signaturesreturns a single flat dict for one config, or a list of dictsfor a list of configs.
Nonefields are stripped.compile_post_execution_signatures(pre, tool_output)preserves the input shape (dict in, dict out;list in, list out) and adds
end_time,execution_status, and optionalpartial_results.Anything in
tool_output["extra_signatures"]is merged into the final dict verbatim, useful forinjector-specific fields like
parent_process_nameor custom signal types.Failure modes
compile_pre_execution_signaturesValueErrorNetwork+Cloud)ValueErrorNetworkInjectorConfigwith zero or more than one identity fieldValidationErrorbuild_network_configsitem that's neitherstr,dict, nor aNetworkInjectorConfigTypeErrortool_outputin post-executionOpenAEVErrorLifecycle Flow
sequenceDiagram participant Injector participant SM as SignatureManager participant API as SignatureApiManager participant Backend Injector->>SM: compile_pre_execution_signatures(config) SM-->>Injector: pre_signatures dict/list Note over Injector: Tool executes... Injector->>SM: compile_post_execution_signatures(pre, tool_output) SM-->>Injector: merged signatures Injector->>SM: build_payload(post, target_meta, expectation_type) SM-->>Injector: nested wire payload Injector->>SM: send_signatures(inject_id, phase, signatures) SM->>API: send_signatures(inject_id, phase, signatures) API->>API: validate + normalize + chunk if needed API->>Backend: POST /api/injects/{id}/callback Backend-->>API: 200/202Transport Behaviour
max_payload_size(default 1 MiB) are split by target and sent sequentially withchunk_index/total_chunksmetadata.SignatureTransmissionErrorimmediately.Wire Format
Payloads follow the nested schema expected by the callback endpoint:
{ "phase": "execution_complete", "expectation_signature": { "targets": [ { "signature_target": { "agent": "...", "asset": "...", "asset_group": "..." }, "signature_values": [ { "expectation_type": "DETECTION", "values": [ { "signature_type": "source_ipv4", "signature_value": "172.17.0.2" }, { "signature_type": "target_ipv4", "signature_value": "10.0.0.1" }, { "signature_type": "start_time", "signature_value": "2024-06-26T06:06:06Z" }, { "signature_type": "end_time", "signature_value": "2024-06-26T06:06:09Z" }, { "signature_type": "execution_status", "signature_value": "success" } ] } ] } ] } }Known
signature_typelabels live inpyoaev.signatures.SignatureTypes(
source_ipv4_address,target_hostname_address,cloud_region,query, ...). The wire formatitself accepts any string, so injectors are free to add custom types via
tool_output.extra_signatures.Utility
Resolution strategy:
CONTAINER_IPenv var >socket.gethostbyname>hostname -i>"unknown".The result is cached for the lifetime of the manager and IPv6 is sniffed best-effort alongside.
Related issues
ContractOutputType:ExpectationSignature#206Checklist
Further comments