From 02e0123056ac3b4a5d99916cf75df08eebbf240b Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 13 Jul 2026 21:28:51 +0200 Subject: [PATCH 1/5] lua/http: allow to take http2 txs but restrict lua further functions to http1 for now Next commits will add http2 compatibility function by function Ticket: 6409 --- src/util-lua-http.c | 48 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/src/util-lua-http.c b/src/util-lua-http.c index f5599a4bb5..3fdf14d833 100644 --- a/src/util-lua-http.c +++ b/src/util-lua-http.c @@ -31,16 +31,21 @@ static const char htp_tx[] = "suricata:http:tx"; struct LuaTx { - htp_tx_t *tx; + void *tx; + AppProto alproto; }; static int LuaHttpGetTx(lua_State *luastate) { - if (!LuaStateNeedProto(luastate, ALPROTO_HTTP1)) { + Flow *flow = LuaStateGetFlow(luastate); + if (flow == NULL) + return LuaCallbackError(luastate, "internal error: no flow"); + + if (flow->alproto != ALPROTO_HTTP1 && flow->alproto != ALPROTO_HTTP2 && flow->alproto != ALPROTO_DOH2) { return LuaCallbackError(luastate, "error: protocol not http"); } - htp_tx_t *tx = LuaStateGetTX(luastate); + void *tx = LuaStateGetTX(luastate); if (tx == NULL) { return LuaCallbackError(luastate, "error: no tx available"); } @@ -50,6 +55,7 @@ static int LuaHttpGetTx(lua_State *luastate) } ltx->tx = tx; + ltx->alproto = flow->alproto; luaL_getmetatable(luastate, htp_tx); lua_setmetatable(luastate, -2); @@ -64,6 +70,10 @@ static int LuaHttpGetRequestHost(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const struct bstr *host = htp_tx_request_hostname(tx->tx); if (host == NULL) { lua_pushnil(luastate); @@ -80,6 +90,10 @@ static int LuaHttpGetRequestUriRaw(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const struct bstr *uri = htp_tx_request_uri(tx->tx); if (uri == NULL) { lua_pushnil(luastate); @@ -96,6 +110,10 @@ static int LuaHttpGetRequestUriNormalized(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } bstr *request_uri_normalized = (bstr *)htp_tx_normalized_uri(tx->tx); if (request_uri_normalized == NULL || bstr_ptr(request_uri_normalized) == NULL || @@ -113,6 +131,10 @@ static int LuaHttpGetRequestLine(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const struct bstr *line = htp_tx_request_line(tx->tx); if (line == NULL) { @@ -130,6 +152,10 @@ static int LuaHttpGetResponseLine(lua_State *luastate) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const struct bstr *line = htp_tx_response_line(tx->tx); if (line == NULL) { @@ -147,6 +173,10 @@ static int LuaHttpGetHeader(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } /* since arg was added at last, it must be on top of the stack */ const char *name = LuaGetStringArgument(luastate, lua_gettop(luastate)); @@ -185,6 +215,10 @@ static int LuaHttpGetRawHeaders(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx->tx); uint8_t *raw = htud->request_headers_raw; @@ -217,6 +251,10 @@ static int LuaHttpGetHeaders(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } const htp_headers_t *table = htp_tx_request_headers(tx->tx); if (dir == 1) @@ -258,6 +296,10 @@ static int LuaHttpGetBody(lua_State *luastate, int dir) lua_pushnil(luastate); return 1; } + if (tx->alproto != ALPROTO_HTTP1) { + lua_pushnil(luastate); + return 1; + } HtpTxUserData *htud = (HtpTxUserData *)htp_tx_get_user_data(tx->tx); From 10ad4c00e1fae667f8f9667c0a681b5ab36d42bd Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Tue, 25 Aug 2026 15:28:56 +0200 Subject: [PATCH 2/5] lua/http: add function to get tx from a specific http version get_http1_tx for HTTP1 and get_http2_tx for HTTP2 Ticket: 6409 --- src/util-lua-http.c | 52 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/src/util-lua-http.c b/src/util-lua-http.c index 3fdf14d833..ba9a05ab0b 100644 --- a/src/util-lua-http.c +++ b/src/util-lua-http.c @@ -35,16 +35,8 @@ struct LuaTx { AppProto alproto; }; -static int LuaHttpGetTx(lua_State *luastate) +static int LuaHttpGetTxProtoChecked(Flow *flow, lua_State *luastate) { - Flow *flow = LuaStateGetFlow(luastate); - if (flow == NULL) - return LuaCallbackError(luastate, "internal error: no flow"); - - if (flow->alproto != ALPROTO_HTTP1 && flow->alproto != ALPROTO_HTTP2 && flow->alproto != ALPROTO_DOH2) { - return LuaCallbackError(luastate, "error: protocol not http"); - } - void *tx = LuaStateGetTX(luastate); if (tx == NULL) { return LuaCallbackError(luastate, "error: no tx available"); @@ -63,6 +55,46 @@ static int LuaHttpGetTx(lua_State *luastate) return 1; } +static int LuaHttpGetTx(lua_State *luastate) +{ + Flow *flow = LuaStateGetFlow(luastate); + if (flow == NULL) + return LuaCallbackError(luastate, "internal error: no flow"); + + if (flow->alproto != ALPROTO_HTTP1 && flow->alproto != ALPROTO_HTTP2 && + flow->alproto != ALPROTO_DOH2) { + return LuaCallbackError(luastate, "error: protocol not http"); + } + + return LuaHttpGetTxProtoChecked(flow, luastate); +} + +static int LuaHttpGetHttp1Tx(lua_State *luastate) +{ + Flow *flow = LuaStateGetFlow(luastate); + if (flow == NULL) + return LuaCallbackError(luastate, "internal error: no flow"); + + if (flow->alproto != ALPROTO_HTTP1) { + return LuaCallbackError(luastate, "error: protocol not http"); + } + + return LuaHttpGetTxProtoChecked(flow, luastate); +} + +static int LuaHttpGetHttp2Tx(lua_State *luastate) +{ + Flow *flow = LuaStateGetFlow(luastate); + if (flow == NULL) + return LuaCallbackError(luastate, "internal error: no flow"); + + if (flow->alproto != ALPROTO_HTTP2 && flow->alproto != ALPROTO_DOH2) { + return LuaCallbackError(luastate, "error: protocol not http"); + } + + return LuaHttpGetTxProtoChecked(flow, luastate); +} + static int LuaHttpGetRequestHost(lua_State *luastate) { struct LuaTx *tx = luaL_testudata(luastate, 1, htp_tx); @@ -371,6 +403,8 @@ static const struct luaL_Reg txlib[] = { static const struct luaL_Reg htplib[] = { // clang-format off {"get_tx", LuaHttpGetTx }, + {"get_http1_tx", LuaHttpGetHttp1Tx }, + {"get_http2_tx", LuaHttpGetHttp2Tx }, {NULL, NULL,}, // clang-format on }; From d66d48fdbf338cfd8cc182c3156dfb77fd75a4d6 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Mon, 13 Jul 2026 22:12:32 +0200 Subject: [PATCH 3/5] lua/http2: support request_uri_raw function Ticket: 6409 --- src/detect-lua.c | 13 +++++++++++++ src/util-lua-http.c | 14 ++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/src/detect-lua.c b/src/detect-lua.c index 39b8232558..29d10c5d70 100644 --- a/src/detect-lua.c +++ b/src/detect-lua.c @@ -75,6 +75,7 @@ static void DetectLuaRegisterTests(void); static void DetectLuaFree(DetectEngineCtx *, void *); static int g_lua_ja3_list_id = 0; static int g_lua_ja3s_list_id = 0; +static int g_lua_http_uri_list_id = 0; /** * \brief Registration function for keyword: lua @@ -104,6 +105,12 @@ void DetectLuaRegister(void) DetectAppLayerInspectEngineRegister( "ja3s.lua", ALPROTO_QUIC, SIG_FLAG_TOCLIENT, 1, DetectEngineInspectGenericList, NULL); + g_lua_http_uri_list_id = DetectBufferTypeRegister("http.uri.lua"); + DetectAppLayerInspectEngineRegister("http.uri.lua", ALPROTO_HTTP1, SIG_FLAG_TOSERVER, + HTP_REQUEST_PROGRESS_LINE, DetectEngineInspectGenericList, NULL); + DetectAppLayerInspectEngineRegisterSubState("http.uri.lua", ALPROTO_HTTP2, SIG_FLAG_TOSERVER, + HTTP2TxTypeStream, HTTP2ProgHeaders, DetectEngineInspectGenericList, NULL); + SCLogDebug("registering lua rule option"); } @@ -112,6 +119,7 @@ void DetectLuaRegister(void) #define FLAG_DATATYPE_PAYLOAD BIT_U32(1) #define FLAG_LIST_JA3 BIT_U32(3) #define FLAG_LIST_JA3S BIT_U32(4) +#define FLAG_LIST_HTTP_URI BIT_U32(5) #define FLAG_ERROR_LOGGED BIT_U32(23) #define FLAG_BLOCKED_FUNCTION_LOGGED BIT_U32(24) #define FLAG_INSTRUCTION_LIMIT_LOGGED BIT_U32(25) @@ -659,6 +667,9 @@ static int DetectLuaSetupPrime(DetectEngineCtx *de_ctx, DetectLuaData *ld, const ld->flags |= FLAG_LIST_JA3; } else if (strcmp(k, "ja3s") == 0) { ld->flags |= FLAG_LIST_JA3S; + } else if (strcmp(k, "http.uri") == 0) { + // TODO/DRAFT/decision-required: Should this rather be named http.request_line ? + ld->flags |= FLAG_LIST_HTTP_URI; } else if (strcmp(k, "packet") == 0) { ld->flags |= FLAG_DATATYPE_PACKET; } else if (strcmp(k, "payload") == 0) { @@ -770,6 +781,8 @@ static int DetectLuaSetup (DetectEngineCtx *de_ctx, Signature *s, const char *st list = g_lua_ja3_list_id; } else if (lua->flags & FLAG_LIST_JA3S) { list = g_lua_ja3s_list_id; + } else if (lua->flags & FLAG_LIST_HTTP_URI) { + list = g_lua_http_uri_list_id; } } diff --git a/src/util-lua-http.c b/src/util-lua-http.c index ba9a05ab0b..79ba0ae715 100644 --- a/src/util-lua-http.c +++ b/src/util-lua-http.c @@ -122,10 +122,16 @@ static int LuaHttpGetRequestUriRaw(lua_State *luastate) lua_pushnil(luastate); return 1; } - if (tx->alproto != ALPROTO_HTTP1) { - lua_pushnil(luastate); - return 1; - } + if (tx->alproto == ALPROTO_HTTP2 || tx->alproto == ALPROTO_DOH2) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (SCHttp2TxGetUri(tx->tx, &b, &b_len) != 1) { + lua_pushnil(luastate); + return 1; + } + return LuaPushStringBuffer(luastate, b, b_len); + } // else ALPROTO_HTTP1 const struct bstr *uri = htp_tx_request_uri(tx->tx); if (uri == NULL) { lua_pushnil(luastate); From b8b321e7ef20b5ca82dea06439a6b137f162d7a1 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Sun, 11 Oct 2026 20:29:30 +0200 Subject: [PATCH 4/5] lua/http2: support request_uri_normalized function Ticket: 6409 --- src/util-lua-http.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/util-lua-http.c b/src/util-lua-http.c index 79ba0ae715..750452628d 100644 --- a/src/util-lua-http.c +++ b/src/util-lua-http.c @@ -148,10 +148,17 @@ static int LuaHttpGetRequestUriNormalized(lua_State *luastate) lua_pushnil(luastate); return 1; } - if (tx->alproto != ALPROTO_HTTP1) { - lua_pushnil(luastate); - return 1; - } + if (tx->alproto == ALPROTO_HTTP2 || tx->alproto == ALPROTO_DOH2) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (SCHttp2TxGetUri(tx->tx, &b, &b_len) != 1) { + lua_pushnil(luastate); + return 1; + } + return LuaPushStringBuffer(luastate, b, b_len); + } // else ALPROTO_HTTP1 + bstr *request_uri_normalized = (bstr *)htp_tx_normalized_uri(tx->tx); if (request_uri_normalized == NULL || bstr_ptr(request_uri_normalized) == NULL || From cdbaa8b7a120d3652ff40ebfad346e33f6cdac37 Mon Sep 17 00:00:00 2001 From: Philippe Antoine Date: Sun, 11 Oct 2026 20:38:12 +0200 Subject: [PATCH 5/5] lua/http2: support request_line function Ticket: 6409 --- src/util-lua-http.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/util-lua-http.c b/src/util-lua-http.c index 750452628d..ccb3f45347 100644 --- a/src/util-lua-http.c +++ b/src/util-lua-http.c @@ -176,10 +176,16 @@ static int LuaHttpGetRequestLine(lua_State *luastate) lua_pushnil(luastate); return 1; } - if (tx->alproto != ALPROTO_HTTP1) { - lua_pushnil(luastate); - return 1; - } + if (tx->alproto == ALPROTO_HTTP2 || tx->alproto == ALPROTO_DOH2) { + uint32_t b_len = 0; + const uint8_t *b = NULL; + + if (SCHttp2TxGetRequestLine(tx->tx, &b, &b_len) != 1) { + lua_pushnil(luastate); + return 1; + } + return LuaPushStringBuffer(luastate, b, b_len); + } // else ALPROTO_HTTP1 const struct bstr *line = htp_tx_request_line(tx->tx); if (line == NULL) {