Skip to content

Commit 88bc261

Browse files
committed
fix: align Bitdefender GravityZone filter and rule contracts
1 parent 6c3af7e commit 88bc261

10 files changed

Lines changed: 271 additions & 14 deletions

‎filters/antivirus/bitdefender_gz.yml‎

Lines changed: 52 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -504,8 +504,8 @@ pipeline:
504504

505505
- rename:
506506
from:
507-
- log.severity
508-
to: severity
507+
- log.severity
508+
to: log.cefSeverity
509509

510510
- rename:
511511
from:
@@ -560,23 +560,30 @@ pipeline:
560560

561561
- rename:
562562
from:
563-
- log.BitdefenderGZEventSourceIP
563+
- log.BitdefenderGZEventSourceIP
564564
to: origin.ip
565+
where: '!exists("origin.ip")'
565566

566567
# Adding actionResult field to indicate whether the action was successful or failed
568+
- add:
569+
function: string
570+
params:
571+
key: actionResult
572+
value: denied
573+
where: oneOf("action", ["blocked", "block", "aph_blocked", "portscan_blocked", "quarantined"])
567574
- add:
568575
function: string
569576
params:
570577
key: actionResult
571578
value: success
572-
where: 'oneOf("action", ["blocked", "block", "aph_blocked", "portscan_blocked", "deleted", "disinfected", "quarantined", "restored"])'
579+
where: oneOf("action", ["deleted", "disinfected", "restored"])
573580

574581
- add:
575582
function: string
576583
params:
577584
key: actionResult
578-
value: failed
579-
where: 'oneOf("action", ["still present", "ignored", "no action", "reportOnly"])'
585+
value: failure
586+
where: oneOf("action", ["still present", "ignored", "no action", "reportOnly"])
580587

581588
# Adding geolocation to origin ip
582589
- dynamic:
@@ -620,4 +627,42 @@ pipeline:
620627
- log.dvc
621628
- log.request
622629
- log.suser
623-
- log.fname
630+
- log.fname
631+
632+
# Keep addresses in IP fields and retain other source values under log.
633+
- rename:
634+
from:
635+
- origin.ip
636+
to: log.unparsedOriginIp
637+
where: exists("origin.ip") && (!(inCIDR("origin.ip","0.0.0.0/0") || inCIDR("origin.ip","::/0")) || oneOf("origin.ip",["0.0.0.0","::"]))
638+
- rename:
639+
from:
640+
- target.ip
641+
to: log.unparsedTargetIp
642+
where: exists("target.ip") && (!(inCIDR("target.ip","0.0.0.0/0") || inCIDR("target.ip","::/0")) || oneOf("target.ip",["0.0.0.0","::"]))
643+
644+
# Normalize the source event severity.
645+
- add:
646+
function: string
647+
params:
648+
key: severity
649+
value: info
650+
where: (greaterOrEqual("log.cefSeverity",0) && lessOrEqual("log.cefSeverity",3)) || oneOf("log.cefSeverity",["Low","low","Unknown"])
651+
- add:
652+
function: string
653+
params:
654+
key: severity
655+
value: warning
656+
where: (greaterOrEqual("log.cefSeverity",4) && lessOrEqual("log.cefSeverity",6)) || oneOf("log.cefSeverity",["Medium","medium"])
657+
- add:
658+
function: string
659+
params:
660+
key: severity
661+
value: error
662+
where: (greaterOrEqual("log.cefSeverity",7) && lessOrEqual("log.cefSeverity",8)) || oneOf("log.cefSeverity",["High","high"])
663+
- add:
664+
function: string
665+
params:
666+
key: severity
667+
value: critical
668+
where: (greaterOrEqual("log.cefSeverity",9) && lessOrEqual("log.cefSeverity",10)) || oneOf("log.cefSeverity",["Very-High","Very High","very-high"])

‎filters/audits/bitdefender.md‎

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Bitdefender GravityZone normalization and rule review
2+
3+
Preserve attacker IPs, normalize response outcomes and severity, and align phishing/priority rules.
4+
5+
This draft targets UTMStack `v11`. It contains 1 filter changes
6+
and 7 rule changes for this technology only. Review covered
7+
1 filter configurations and 21 matching shipped rule files.
8+
Unchanged rules are listed in the regression manifest; they are not duplicated in the diff.
9+
10+
## Contract and validation
11+
12+
- Compared exact standard names/types with go-sdk v1.1.31 and the supplied UTMStack dictionaries.
13+
- Checked documented pipeline ordering, rename/move behavior, open vendor log fields,
14+
event-side versus alert-side fields, and surviving fields used by affected rule predicates/history/grouping.
15+
- Strict SDK configuration decoding and actual CEL compilation pass for this scope.
16+
- 8 synthetic normalization cases pass, including SDK Event conversion and any
17+
trigger predicate assertions recorded in the manifest.
18+
- The scoped alerts module tests and `git diff --check` pass with the shared contract runner applied.
19+
20+
The shared alert-contract PR supplies the reusable Go runner for the manifest in
21+
`plugins/alerts/testdata/filter-contracts/bitdefender.json`. Apply that support before running `go test ./...` in `plugins/alerts`.
22+
23+
The changed rules also require the shared alert-grouping fix to resolve `lastEvent.*` values correctly at runtime.
24+
25+
The model starts from synthetic extraction results. It does not run complex grok,
26+
JSON/KV/XML/CSV extraction, time conversion, dynamic plugins, historical OpenSearch
27+
queries, or the closed EventProcessor. Raw vendor logs and resulting alerts must
28+
still be checked in staging before rollout. No customer false-positive reduction
29+
has been measured and no production rollout is included.
30+
31+
32+
33+
## References
34+
35+
- [SDK schema](https://github.com/threatwinds/go-sdk/blob/v1.1.31/plugins/plugins.proto)
36+
- [Filter steps](https://github.com/threatwinds/go-sdk/wiki/Filter-Steps-Reference)
37+
- [Standard event schema](https://github.com/threatwinds/go-sdk/wiki/Standard-Event-Schema)
38+
- [Rule implementation](https://github.com/threatwinds/go-sdk/wiki/Implementing-Rules)
39+
40+
`afterEvents`, empty noncapturing grok names, supported numeric strings, and custom
41+
`log.*` fields are accepted. Existing textual protocol casing and vendor action names
42+
are preserved unless a concrete consumer mismatch requires correction.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
{
2+
"technology": "Bitdefender GravityZone",
3+
"filters": [
4+
"filters/antivirus/bitdefender_gz.yml"
5+
],
6+
"rules": [
7+
"rules/antivirus/bitdefender_gz/antivirus_service_stopped.yml",
8+
"rules/antivirus/bitdefender_gz/apt_detection.yml",
9+
"rules/antivirus/bitdefender_gz/av_console_lateral_movement.yml",
10+
"rules/antivirus/bitdefender_gz/av_policy_override.yml",
11+
"rules/antivirus/bitdefender_gz/bootkit_detection.yml",
12+
"rules/antivirus/bitdefender_gz/crypto_mining_detection.yml",
13+
"rules/antivirus/bitdefender_gz/email_threat_spreading.yml",
14+
"rules/antivirus/bitdefender_gz/fileless_malware_detection.yml",
15+
"rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml",
16+
"rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml",
17+
"rules/antivirus/bitdefender_gz/memory_threat_detection.yml",
18+
"rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml",
19+
"rules/antivirus/bitdefender_gz/network_threat_detection.yml",
20+
"rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml",
21+
"rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml",
22+
"rules/antivirus/bitdefender_gz/ransomware_behavior_detection.yml",
23+
"rules/antivirus/bitdefender_gz/realtime_protection_disabled.yml",
24+
"rules/antivirus/bitdefender_gz/rootkit_detection.yml",
25+
"rules/antivirus/bitdefender_gz/suspicious_exclusions_added.yml",
26+
"rules/antivirus/bitdefender_gz/usb_malware_propagation.yml",
27+
"rules/antivirus/bitdefender_gz/zero_day_malware_detection.yml"
28+
],
29+
"fixtures": [
30+
{
31+
"name": "Bitdefender phishing aph_blocked",
32+
"filter": "antivirus/bitdefender_gz.yml",
33+
"input": {
34+
"log": {
35+
"actFull": "aph_blocked",
36+
"BitdefenderGZModule": "aph"
37+
}
38+
},
39+
"expected": {
40+
"actionResult": "denied"
41+
},
42+
"absent": [],
43+
"rules": {
44+
"rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": false
45+
}
46+
},
47+
{
48+
"name": "Bitdefender phishing reportOnly",
49+
"filter": "antivirus/bitdefender_gz.yml",
50+
"input": {
51+
"log": {
52+
"actFull": "reportOnly",
53+
"BitdefenderGZModule": "aph"
54+
}
55+
},
56+
"expected": {
57+
"actionResult": "failure"
58+
},
59+
"absent": [],
60+
"rules": {
61+
"rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml": true
62+
}
63+
},
64+
{
65+
"name": "Bitdefender attacker priority",
66+
"filter": "antivirus/bitdefender_gz.yml",
67+
"input": {
68+
"log": {
69+
"BitdefenderGZDetectionAttackerIp": "198.51.100.10",
70+
"BitdefenderGZEventSourceIP": "10.0.0.2"
71+
}
72+
},
73+
"expected": {
74+
"origin.ip": "198.51.100.10"
75+
},
76+
"absent": [],
77+
"rules": {}
78+
},
79+
{
80+
"name": "Bitdefender CEF priority 0",
81+
"filter": "antivirus/bitdefender_gz.yml",
82+
"input": {
83+
"log": {
84+
"severity": "0",
85+
"BitdefenderGZModule": "network-monitor"
86+
}
87+
},
88+
"expected": {
89+
"severity": "info",
90+
"log.cefSeverity": "0"
91+
},
92+
"absent": [],
93+
"rules": {
94+
"rules/antivirus/bitdefender_gz/network_threat_detection.yml": false
95+
}
96+
},
97+
{
98+
"name": "Bitdefender CEF priority 3",
99+
"filter": "antivirus/bitdefender_gz.yml",
100+
"input": {
101+
"log": {
102+
"severity": "3",
103+
"BitdefenderGZModule": "network-monitor"
104+
}
105+
},
106+
"expected": {
107+
"severity": "info",
108+
"log.cefSeverity": "3"
109+
},
110+
"absent": [],
111+
"rules": {
112+
"rules/antivirus/bitdefender_gz/network_threat_detection.yml": false
113+
}
114+
},
115+
{
116+
"name": "Bitdefender CEF priority 6",
117+
"filter": "antivirus/bitdefender_gz.yml",
118+
"input": {
119+
"log": {
120+
"severity": "6",
121+
"BitdefenderGZModule": "network-monitor"
122+
}
123+
},
124+
"expected": {
125+
"severity": "warning",
126+
"log.cefSeverity": "6"
127+
},
128+
"absent": [],
129+
"rules": {
130+
"rules/antivirus/bitdefender_gz/network_threat_detection.yml": false
131+
}
132+
},
133+
{
134+
"name": "Bitdefender CEF priority 8",
135+
"filter": "antivirus/bitdefender_gz.yml",
136+
"input": {
137+
"log": {
138+
"severity": "8",
139+
"BitdefenderGZModule": "network-monitor"
140+
}
141+
},
142+
"expected": {
143+
"severity": "error",
144+
"log.cefSeverity": "8"
145+
},
146+
"absent": [],
147+
"rules": {
148+
"rules/antivirus/bitdefender_gz/network_threat_detection.yml": true
149+
}
150+
},
151+
{
152+
"name": "Bitdefender CEF priority 10",
153+
"filter": "antivirus/bitdefender_gz.yml",
154+
"input": {
155+
"log": {
156+
"severity": "10",
157+
"BitdefenderGZModule": "network-monitor"
158+
}
159+
},
160+
"expected": {
161+
"severity": "critical",
162+
"log.cefSeverity": "10"
163+
},
164+
"absent": [],
165+
"rules": {
166+
"rules/antivirus/bitdefender_gz/network_threat_detection.yml": true
167+
}
168+
}
169+
]
170+
}

‎rules/antivirus/bitdefender_gz/apt_detection.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ description: |
4040
5. Collect forensic artifacts before remediating - memory image and endpoint logs - since a targeted intrusion warrants attribution work
4141
6. Isolate the endpoint if the detection action shows the threat was not blocked, then hunt for what ran while it was active
4242
where: |
43-
greaterOrEqual("severity", 8) &&
43+
(greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) &&
4444
(
4545
(equals("log.BitdefenderGZModule", "hd") &&
4646
regexMatch("log.BitdefenderGZAttackTypes", "(?i)targeted attack")) ||

‎rules/antivirus/bitdefender_gz/high_severity_threat_detection.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ description: |
3838
- Check that signatures were current at deviceTime, using log.BitdefenderGZSignaturesNumber
3939
where: |
4040
oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) &&
41-
greaterOrEqual("severity", 8)
41+
(greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8))
4242
groupBy:
4343
- target.host
4444
- target.malware

‎rules/antivirus/bitdefender_gz/malware_outbreak_multiple_hosts.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ description: |
3333
7. Keep the incident open until a full day passes with no new host reporting the same malware
3434
where: |
3535
oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) &&
36-
greaterOrEqual("severity", 8) &&
36+
(greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8)) &&
3737
exists("target.malware")
3838
correlation:
3939
- indexPattern: v11-log-antivirus-bitdefender-gz-*

‎rules/antivirus/bitdefender_gz/multiple_malware_from_single_source.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ description: |
3535
7. Reimage if the same host keeps reappearing in this rule across days
3636
where: |
3737
oneOf("log.BitdefenderGZModule", ["av", "avc", "hd"]) &&
38-
greaterOrEqual("severity", 8)
38+
(greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8))
3939
correlation:
4040
- indexPattern: v11-log-antivirus-bitdefender-gz-*
4141
within: 1h

‎rules/antivirus/bitdefender_gz/network_threat_detection.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ description: |
3737
6. Block the source at the perimeter, and only then close the alert. A blocked attempt means this attack failed, not that the attacker stopped
3838
where: |
3939
oneOf("log.BitdefenderGZModule", ["network-monitor", "fw"]) &&
40-
greaterOrEqual("severity", 8)
40+
(greaterOrEqual("log.cefSeverity", 8) || greaterOrEqual("severity", 8))
4141
correlation:
4242
- indexPattern: v11-log-antivirus-bitdefender-gz-*
4343
within: 2h

‎rules/antivirus/bitdefender_gz/phishing_access_blocked.yaml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ description: |
3737
7. Submit the URL for blocking at the perimeter so the rest of the estate is covered
3838
where: |
3939
equals("log.BitdefenderGZModule", "aph") &&
40-
equals("actionResult", "success")
40+
equals("action", "reportOnly")
4141
groupBy:
4242
- target.user
4343
- adversary.url

‎rules/antivirus/bitdefender_gz/quarantine_failure_detection.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ description: |
4242
where: |
4343
equals("log.eventType", "AntiMalware") &&
4444
(
45-
equals("actionResult", "failed") ||
45+
oneOf("actionResult", ["failure", "failed"]) ||
4646
(greaterOrEqual("log.BitdefenderGZPresentMalwareCnt", 1) &&
4747
equals("log.BitdefenderGZQuarantinedMalwareCnt", 0) &&
4848
equals("log.BitdefenderGZCleanedMalwareCnt", 0))

0 commit comments

Comments
 (0)