Requirement Document — Rule Engine for Assets
| Status | Draft |
| Owner | Ayush Aggarwal |
| Last updated | 2026-06-16 |
| Affected components | source/models/rule_engine.py, source/tasks.py, source/serializers/rule_engine.py, source/signals/triggers.py, source/managers.py, source/models/asset.py |
1. Overview
The Rule Engine lets users define Rules (a data_type + conditions) that, when matching
objects are created or changed, fan out Actions (create ticket, notify, change status, etc.).
Today the engine supports Finding, Check, and DataList only.
This document specifies extending the engine to operate in the context of Assets, so users
can be notified and act when assets appear or change (e.g. a new unmanaged AI asset, an asset
tagged Public), including actions that target the asset’s findings.
2. Background & problem statement
The engine fires from the manager_method_called signal, emitted by RuleEngineCustomManager
on create/update/bulk writes (source/managers.py). run_rule_engine (source/tasks.py:2838)
matches the changed objects against each enabled rule via FILTER_SET_MAPPING, then dispatches
actions through the transactional outbox (OutboxEvents).
Current limitation: there is no way to react to asset lifecycle changes. Users cannot be notified when an asset is newly discovered or when an asset’s attributes (e.g. tags) change.
3. Goals / non-goals
Goals
- Add an
Is Newconcept for Assets (and reuse it for Findings). - Allow Rules with
data_type = Asset. - Support actions triggered by asset matches, including actions that apply to the asset’s findings.
- Support count/threshold conditions (“total findings > / < X”).
Non-goals
- Reworking the notification delivery layer.
- Changing the Finding/Check/DataList rule behaviour beyond the shared
is_newaddition. - A general cross-model rule language (only the specific Asset↔Finding hop in scope).
4. Glossary
| Term | Meaning |
|---|---|
| Rule | data_type + JSON conditions, with one or more Actions. |
| Action | An executable (create_ticket, change_status, …) with arguments. |
| Trigger | A DB write routed through RuleEngineCustomManager that emits manager_method_called. |
is_new |
Marker indicating an asset/finding was discovered in the current scan cycle. |
| Outbox | OutboxEvents rows that drive reliable, transactional action dispatch. |
5. Functional requirements
FR-1 — Is New for Assets and Findings
- FR-1.1 Add an
is_newcapability for Assets. Decision required (see Open Question OQ-1): derived (created within window) or persisted boolean with an explicit reset step. - FR-1.2 Reuse / expose
is_newfor Findings (Finding already hasdate_discovered,source/models/vulnerability.py:195). - FR-1.3 If persisted: define who clears the flag and when (e.g. end of scan cycle or on acknowledgement), and guarantee a “new” object notifies at most once (idempotency).
- FR-1.4
is_newmust be filterable inAssetFilterSet/FindingDashboardFilterSetso it can be used as a rule condition.
FR-2 — Rule Engine runs in the context of Assets
- FR-2.1 Add
AssettoRule.DataTypeChoices. - FR-2.2 Register
Asset: AssetFilterSetinFILTER_SET_MAPPING(filterset exists atsource/filters/asset.py:44). - FR-2.3 Ensure asset writes trigger the engine.
Assetcurrently uses the default manager and does not emitmanager_method_called. Wire it viaRuleEngineCustomManageror an explicit trigger. Must account for bulk scan ingestion volume (see NFR-1). - FR-2.4 Tag changes must trigger evaluation. Asset tags are a
GenericRelationviaTagItem; adding a tag does not write theAssetrow, so aTagItem-level trigger scoped to assets is required (covers thePublictag use case).
FR-3 — Actions
- FR-3.1 Create Ticket — reuse
rule_engine_create_ticket. - FR-3.2 Send to Notification — reuse
rule_engine_send_to_cwpp_channels. - FR-3.3 Change Status / Change Severity / Ignore — these operate on Findings. When the
matched
data_typeisAsset, the engine must resolve asset → findings (Finding.objects.filter(asset_id__in=...)) before the action runs. Resolution should live in the action dispatch keyed bymodel_name, not inline inrun_rule_engine. - FR-3.4 Register valid Asset actions in
ACTION_MAPPINGSso serializer validation accepts them (source/serializers/rule_engine.py:22). - FR-3.5 All new action tasks must follow the existing contract:
autoretry_for,retry_kwargs, outbox-driven dispatch, and RabbitMQ audit logging viasend_notification_log_to_rabbitmq. - FR-3.6 (Recommended) Add asset-native actions: add tag, assign owner, add to group/label.
FR-4 — New condition types
- FR-4.1 Threshold condition: total findings for an asset
>/<X. This is an aggregate, not a per-object filter — it needs a grouped count query and a dedicated evaluation path (the current engine matches per object viafilterset.qs). - FR-4.2 Cross-entity condition: Finding
is_newAND Assetis_new. Modelled as a Finding rule with asset-prefixed filters joiningFinding.asset, or an Asset rule carrying a nested finding condition. Pick one shape (see OQ-2).
FR-5 — Action precedence & dedup
- FR-5.1 Define precedence when a rule both ignores a finding and creates a ticket/notifies.
- FR-5.2 De-duplicate notifications for
is_newmatches during bulk ingest (per-rule cooldown or batched summary, reusingcreate_rule_engine_execution_notification).
6. Non-functional requirements
- NFR-1 — Throughput: Asset/finding writes occur in bulk during scans. Triggering must not enqueue an unbounded number of rule-engine tasks; batch where possible and avoid per-row fan-out.
- NFR-2 — Reliability: Actions continue to dispatch via the transactional outbox; no action is lost or double-executed on retry.
- NFR-3 — Multi-tenancy: All evaluation and dispatch run within the correct tenant schema
(
replica_aware_schema_context); no cross-tenant leakage. - NFR-4 — Auditability: Every executed action emits a structured RabbitMQ log entry.
- NFR-5 — Backward compatibility: Existing Finding/Check/DataList rules behave unchanged.
7. Use cases
| # | Scenario | Trigger | Condition | Action | New work |
|---|---|---|---|---|---|
| UC-1 | New unmanaged AI asset | Asset create | asset_type = Unmanaged AI AND is_new |
Send notification | FR-1, FR-2.1/2.2/2.3 |
| UC-2 | Public tag added to asset |
TagItem create on asset | tag = Public |
Notify / asset action | FR-2.4 |
| UC-3 | New finding on new asset | Finding create | Finding is_new AND Asset is_new |
Notify / finding action | FR-4.2 |
| UC-4 | Asset finding count crosses threshold | Finding create/update | count of findings >/< X |
Notify / ticket | FR-4.1 |
8. Data model changes (indicative)
Asset: addis_new(and/orfirst_seen) per OQ-1; switch to / wrap withRuleEngineCustomManagerper FR-2.3.Rule.DataTypeChoices: addASSET = "Asset".- Registries:
FILTER_SET_MAPPING,ACTION_MAPPINGS,ACTION_TASK_MAPPINGextended for Asset. - Migrations must use
SmartAddIndex/SmartRemoveIndexif any index is added.
9. Risks
| Risk | Impact | Mitigation |
|---|---|---|
| Asset trigger not wired | Rules silently never fire | FR-2.3 explicit wiring + test |
Stale is_new flag |
Repeated/duplicate notifications | FR-1.3 reset + idempotency |
| Bulk-ingest task storm | Worker saturation | NFR-1 batching, FR-5.2 dedup |
| Asset→Finding action hop missed | Status/severity/ignore no-op for asset rules | FR-3.3 dispatch-level resolution |
10. Open questions
- OQ-1: Is
is_newderived (created within a window) or persisted (boolean + reset)? - OQ-2: Is the cross-entity rule (UC-3) modelled as a Finding rule with asset filters, or an Asset rule with a nested finding condition?
- OQ-3: For UC-4, is the threshold evaluated per asset, per asset-type, or globally?
- OQ-4: Should
is_newnotifications be per-object or batched per scan?
11. Out of scope / future
- General multi-model rule composition beyond Asset↔Finding.
- UI for rule authoring (assumed handled separately).