From c166eed11700845936c9a1e39f1b3591fa0106d5 Mon Sep 17 00:00:00 2001 From: Jeff Lucovsky Date: Wed, 22 Oct 2025 10:57:46 -0400 Subject: [PATCH] test/luaxform: Ensure ip.src/ip.dst work with transforms Demonstrate that the sticky buffers ip.src/ip.dst work with transforms. The sample lua script creates a string "cidr" from an ipv4 src/dst address. E.g., "1.2.3.0/24" The tests require 9.0 or greater; when issue 8016 is addressed, additional version constraints will be added. Issue: 8015 --- tests/lua/lua-transform-09/README.md | 1 + tests/lua/lua-transform-09/test.rules | 2 + tests/lua/lua-transform-09/test.yaml | 22 ++++++ tests/lua/lua-transform-09/transform.lua | 90 ++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 tests/lua/lua-transform-09/README.md create mode 100644 tests/lua/lua-transform-09/test.rules create mode 100644 tests/lua/lua-transform-09/test.yaml create mode 100644 tests/lua/lua-transform-09/transform.lua diff --git a/tests/lua/lua-transform-09/README.md b/tests/lua/lua-transform-09/README.md new file mode 100644 index 000000000..801afba05 --- /dev/null +++ b/tests/lua/lua-transform-09/README.md @@ -0,0 +1 @@ +Ensure Lua transform works with the ip.src/ip.dst sticky buffers. diff --git a/tests/lua/lua-transform-09/test.rules b/tests/lua/lua-transform-09/test.rules new file mode 100644 index 000000000..dc4896ac7 --- /dev/null +++ b/tests/lua/lua-transform-09/test.rules @@ -0,0 +1,2 @@ +alert http any any -> any any (msg: "test ip cdr script"; flow:established; ip.src; luaxform: transform.lua, 0xffffff00; content:"10.20.48.0/24";sid: 1;) +alert http any any -> any any (msg: "test ip cdr script"; flow:established; ip.dst; luaxform: transform.lua, 0xffff0000; content:"10.50.0.0/16";sid: 2;) diff --git a/tests/lua/lua-transform-09/test.yaml b/tests/lua/lua-transform-09/test.yaml new file mode 100644 index 000000000..4f3ef65ca --- /dev/null +++ b/tests/lua/lua-transform-09/test.yaml @@ -0,0 +1,22 @@ +requires: + min-version: 9 + +args: + - --set default-rule-path=${TEST_DIR} + - --set security.lua.allow-rules=true + +pcap: ../lua-transform-01/test.pcap + +checks: + + - filter: + count: 4 + match: + event_type: alert + alert.signature_id: 1 + + - filter: + count: 4 + match: + event_type: alert + alert.signature_id: 2 diff --git a/tests/lua/lua-transform-09/transform.lua b/tests/lua/lua-transform-09/transform.lua new file mode 100644 index 000000000..df8b762bd --- /dev/null +++ b/tests/lua/lua-transform-09/transform.lua @@ -0,0 +1,90 @@ +-- luaxform: Convert IPv4 + hex/netmask to CIDR +-- Example: input="192.168.1.45", arg="0xffffff00" → "192.168.1.0/24" + +-- Trim leading/trailing whitespace +local function trim(s) + return s:match("^%s*(.-)%s*$") +end + +-- Parse a mask string (hex format like 0xffffff00) +local function parse_mask(mask_hex) + if not mask_hex then return nil end + mask_hex = trim(mask_hex) + -- Remove optional 0x/0X prefix + local hex = mask_hex:match("^0[xX](%x+)$") or mask_hex + local num = tonumber(hex, 16) + return num +end + +-- Count the number of 1 bits in a 32-bit number +local function count_bits(n) + local count = 0 + for i = 0, 31 do + if (n & (1 << i)) ~= 0 then count = count + 1 end + end + return count +end + +-- Convert IPv4 string + mask to CIDR notation +local function ip_to_cidr2(ip_str, mask_hex) + if not ip_str or not mask_hex then return nil end + + local mask = parse_mask(mask_hex) + if not mask then return nil end + + local a, b, c, d = ip_str:match("(%d+)%.(%d+)%.(%d+)%.(%d+)") + if not a then return nil end + + local ip = (tonumber(a) << 24) | (tonumber(b) << 16) | (tonumber(c) << 8) | tonumber(d) + local net = ip & mask + local bits = count_bits(mask) + + return string.format("%d.%d.%d.%d/%d", + (net >> 24) & 0xFF, + (net >> 16) & 0xFF, + (net >> 8) & 0xFF, + net & 0xFF, + bits + ) +end + +-- Convert a 4-byte host-order IPv4 address and a mask to CIDR +local function ip_to_cidr(ip_bytes, mask_hex) + if not ip_bytes or #ip_bytes ~= 4 then return nil end + + -- Parse the mask + local mask = parse_mask(mask_hex) + if not mask then return nil end + + -- Convert host-order bytes to IP number + local a, b, c, d = string.byte(ip_bytes, 1, 4) + local ip = (a << 24) | (b << 16) | (c << 8) | d + + -- Compute network + local net = ip & mask + + -- Count prefix length + local bits = count_bits(mask) + + -- Convert back to dotted-decimal + return string.format("%d.%d.%d.%d/%d", + (net >> 24) & 0xFF, + (net >> 16) & 0xFF, + (net >> 8) & 0xFF, + net & 0xFF, + bits + ) +end + +-- Transform an IPv4 address and a netmask into a CIDR +-- example: 1.2.3.4 0xffffff00 returns 1.2.3.0/24 +function transform(input, args) + if not input or not args or not args[1] then + return nil, 0 + end + + local cidr = ip_to_cidr(input, args[1]) + if not cidr then return nil, 0 end + + return cidr, #cidr +end -- 2.47.3