From: James Kaddu Date: Wed, 5 Nov 2025 12:40:16 +0000 (+0300) Subject: tests: add rule check for xbits keyword X-Git-Tag: suricata-7.0.14~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=87fe8adfeed9ff7b4825e7d38159af7f2202eeed;p=thirdparty%2Fsuricata-verify.git tests: add rule check for xbits keyword Related to Task #6351 --- diff --git a/tests/rules/xbits/README.md b/tests/rules/xbits/README.md new file mode 100644 index 000000000..7639d14a2 --- /dev/null +++ b/tests/rules/xbits/README.md @@ -0,0 +1,34 @@ +# Xbits Keyword Engine Analysis Test + +This test verifies the engine analysis output for the `xbits` keyword. + +## Purpose + +Tests that the `xbits` keyword is properly reported in the `rules.json` +output when using `--engine-analysis` mode. + +## Coverage + +This test covers: +- `xbits:set` with different tracking modes (ip_src, ip_dst, ip_pair) +- `xbits:isset` +- `xbits:isnotset` +- `xbits:unset` +- `xbits:toggle` +- `xbits` with expire values (default, 60s, 300s) +- Multiple xbits in a single rule + +The test verifies all four properties exposed by the engine analysis: +- **cmd**: The xbits command (set, isset, isnotset, unset, toggle) +- **name**: The xbit name being tracked +- **track**: The tracking mode (ip_src, ip_dst, ip_pair) +- **expire**: The expiration time in seconds (when specified) + +## Reference + +Similar to the flowbits engine analysis test, but for the xbits keyword +which tracks state across hosts/networks rather than within a single flow. + +## Ticket + +https://redmine.openinfosecfoundation.org/issues/6351 diff --git a/tests/rules/xbits/test.rules b/tests/rules/xbits/test.rules new file mode 100644 index 000000000..6f24b0567 --- /dev/null +++ b/tests/rules/xbits/test.rules @@ -0,0 +1,11 @@ +alert ip any any -> any any (msg:"Xbit set"; xbits:set,xb1,track ip_src; sid:1;) +alert ip any any -> any any (msg:"Xbit set"; xbits:set,xb2,track ip_dst; sid:2;) +alert ip any any -> any any (msg:"Xbit isset"; xbits:isset,xb1,track ip_src; sid:3;) +alert ip any any -> any any (msg:"Xbit isset"; xbits:isset,xb2,track ip_dst; sid:4;) +alert ip any any -> any any (msg:"Xbit isnotset"; xbits:isnotset,xb3,track ip_src; sid:5;) +alert ip any any -> any any (msg:"Xbit unset"; xbits:unset,xb1,track ip_src; sid:6;) +alert ip any any -> any any (msg:"Xbit toggle"; xbits:toggle,xb2,track ip_dst; sid:7;) +alert ip any any -> any any (msg:"Xbit isset ip_pair"; xbits:isset,xb4,track ip_pair; sid:8;) +alert ip any any -> any any (msg:"Xbit set both"; xbits:set,xb5,track ip_src; xbits:set,xb6,track ip_dst; sid:9;) +alert ip any any -> any any (msg:"Xbit set with expire"; xbits:set,xb7,track ip_src,expire 60; sid:10;) +alert ip any any -> any any (msg:"Xbit set with expire 300"; xbits:set,xb8,track ip_dst,expire 300; sid:11;) diff --git a/tests/rules/xbits/test.yaml b/tests/rules/xbits/test.yaml new file mode 100644 index 000000000..702d8ba4e --- /dev/null +++ b/tests/rules/xbits/test.yaml @@ -0,0 +1,114 @@ +requires: + min-version: 9.0 + pcap: false + +args: + - --engine-analysis + +checks: +- filter: + filename: rules.json + count: 1 + match: + id: 1 + lists.postmatch.matches[0].name: "xbits" + lists.postmatch.matches[0].xbits.cmd: "set" + lists.postmatch.matches[0].xbits.name: "xb1" + lists.postmatch.matches[0].xbits.track: "ip_src" +- filter: + filename: rules.json + count: 1 + match: + id: 2 + lists.postmatch.matches[0].name: "xbits" + lists.postmatch.matches[0].xbits.cmd: "set" + lists.postmatch.matches[0].xbits.name: "xb2" + lists.postmatch.matches[0].xbits.track: "ip_dst" +- filter: + filename: rules.json + count: 1 + match: + id: 3 + lists.packet.matches[0].name: "xbits" + lists.packet.matches[0].xbits.cmd: "isset" + lists.packet.matches[0].xbits.name: "xb1" + lists.packet.matches[0].xbits.track: "ip_src" +- filter: + filename: rules.json + count: 1 + match: + id: 4 + lists.packet.matches[0].name: "xbits" + lists.packet.matches[0].xbits.cmd: "isset" + lists.packet.matches[0].xbits.name: "xb2" + lists.packet.matches[0].xbits.track: "ip_dst" +- filter: + filename: rules.json + count: 1 + match: + id: 5 + lists.packet.matches[0].name: "xbits" + lists.packet.matches[0].xbits.cmd: "isnotset" + lists.packet.matches[0].xbits.name: "xb3" + lists.packet.matches[0].xbits.track: "ip_src" +- filter: + filename: rules.json + count: 1 + match: + id: 6 + lists.postmatch.matches[0].name: "xbits" + lists.postmatch.matches[0].xbits.cmd: "unset" + lists.postmatch.matches[0].xbits.name: "xb1" + lists.postmatch.matches[0].xbits.track: "ip_src" +- filter: + filename: rules.json + count: 1 + match: + id: 7 + lists.postmatch.matches[0].name: "xbits" + lists.postmatch.matches[0].xbits.cmd: "toggle" + lists.postmatch.matches[0].xbits.name: "xb2" + lists.postmatch.matches[0].xbits.track: "ip_dst" + lists.postmatch.matches[0].xbits.expire: 30 +- filter: + filename: rules.json + count: 1 + match: + id: 8 + lists.packet.matches[0].name: "xbits" + lists.packet.matches[0].xbits.cmd: "isset" + lists.packet.matches[0].xbits.name: "xb4" + lists.packet.matches[0].xbits.track: "ip_pair" +- filter: + filename: rules.json + count: 1 + match: + id: 9 + lists.postmatch.matches[0].name: "xbits" + lists.postmatch.matches[0].xbits.cmd: "set" + lists.postmatch.matches[0].xbits.name: "xb5" + lists.postmatch.matches[0].xbits.track: "ip_src" + lists.postmatch.matches[1].name: "xbits" + lists.postmatch.matches[1].xbits.cmd: "set" + lists.postmatch.matches[1].xbits.name: "xb6" + lists.postmatch.matches[1].xbits.track: "ip_dst" +- filter: + filename: rules.json + count: 1 + match: + id: 10 + lists.postmatch.matches[0].name: "xbits" + lists.postmatch.matches[0].xbits.cmd: "set" + lists.postmatch.matches[0].xbits.name: "xb7" + lists.postmatch.matches[0].xbits.track: "ip_src" + lists.postmatch.matches[0].xbits.expire: 60 +- filter: + filename: rules.json + count: 1 + match: + id: 11 + lists.postmatch.matches[0].name: "xbits" + lists.postmatch.matches[0].xbits.cmd: "set" + lists.postmatch.matches[0].xbits.name: "xb8" + lists.postmatch.matches[0].xbits.track: "ip_dst" + lists.postmatch.matches[0].xbits.expire: 300