From 878fe2444247c7d90ddf43b873d4830f4dfce011 Mon Sep 17 00:00:00 2001 From: Victor Julien Date: Mon, 5 Oct 2026 01:44:57 +0000 Subject: [PATCH] detect: test the pending LTE accept priority The semantics of a pending `flags |= DE_QUIET; + FAIL_IF(DetectFirewallInitDefaultPolicies(de_ctx) != 0); + + /* TD rule at the hook: must still run while the accept rules are pending */ + Signature *s_td = DetectEngineAppendSig(de_ctx, + "alert http1:request_headers any any -> any any " + "(http.host.raw; content:\"early.example.com\"; sid:1103;)"); + FAIL_IF_NULL(s_td); + /* accept the packet hook for all tcp: suppresses the default drop and lets + * the app-layer rules decide */ + Signature *s_scaf1 = DetectFirewallRuleAppendNew( + de_ctx, "accept:hook tcp:all any any -> any any (flow:not_established; sid:1021;)"); + FAIL_IF_NULL(s_scaf1); + Signature *s_scaf2 = DetectFirewallRuleAppendNew( + de_ctx, "accept:hook tcp:all any any -> any any (flow:established; sid:1022;)"); + FAIL_IF_NULL(s_scaf2); + /* content present from the first segment */ + Signature *s_early = DetectFirewallRuleAppendNew(de_ctx, + "accept:flow,alert http1: any any " + "(http.host.raw; content:\"early.example.com\"; sid:1102;)"); + FAIL_IF_NULL(s_early); + /* the header only arrives in the second segment */ + Signature *s_late = DetectFirewallRuleAppendNew(de_ctx, + "accept:flow,alert http1: any any " + "(http.header; content:\"X-Late: a\"; sid:1101;)"); + FAIL_IF_NULL(s_late); + + SigGroupBuild(de_ctx); + DetectEngineThreadCtx *det_ctx = NULL; + DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx); + FAIL_IF_NULL(det_ctx); + + /* segment 1: request line + host, the header block is open */ + const uint8_t *seg1 = (const uint8_t *)"GET / HTTP/1.1\r\nHost: early.example.com\r\n"; + Packet *p1 = UTHBuildPacket((uint8_t *)seg1, (uint16_t)strlen((const char *)seg1), IPPROTO_TCP); + FAIL_IF_NULL(p1); + p1->flow = &f; + p1->flowflags |= FLOW_PKT_TOSERVER; + p1->flowflags |= FLOW_PKT_ESTABLISHED; + p1->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p1->app_update_direction = STREAM_TOSERVER; + int r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, seg1, + (uint16_t)strlen((const char *)seg1)); + FAIL_IF(r != 0); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p1); + + /* 1101 is pending: 1102 must not have accepted the flow yet */ + FAIL_IF(f.flags & FLOW_ACTION_ACCEPT); + + /* segment 2: the late header completes the block */ + const uint8_t *seg2 = (const uint8_t *)"X-Late: a\r\n\r\n"; + Packet *p2 = UTHBuildPacket((uint8_t *)seg2, (uint16_t)strlen((const char *)seg2), IPPROTO_TCP); + FAIL_IF_NULL(p2); + p2->flow = &f; + p2->flowflags |= FLOW_PKT_TOSERVER; + p2->flowflags |= FLOW_PKT_ESTABLISHED; + p2->flags |= PKT_HAS_FLOW | PKT_STREAM_EST; + p2->app_update_direction = STREAM_TOSERVER; + r = AppLayerParserParse(NULL, alp_tctx, &f, ALPROTO_HTTP1, STREAM_TOSERVER, seg2, + (uint16_t)strlen((const char *)seg2)); + FAIL_IF(r != 0); + SigMatchSignatures(&th_v, de_ctx, det_ctx, p2); + + /* 1101 matched on completion: the flow is accepted by the pending rule */ + FAIL_IF(!(f.flags & FLOW_ACTION_ACCEPT)); + + /* the pending rule won; the early rule never matched. The TD rule ran + * once, on completion. */ + uint32_t late = 0, early = 0, td = 0; + for (uint16_t i = 0; i < det_ctx->alert_queue_size; i++) { + if (det_ctx->alert_queue[i].s == s_late) + late++; + else if (det_ctx->alert_queue[i].s == s_early) + early++; + else if (det_ctx->alert_queue[i].s == s_td) + td++; + } + FAIL_IF(late != 1); + FAIL_IF(early != 0); + FAIL_IF(td != 1); + + UTHFreePackets(&p2, 1); + UTHFreePackets(&p1, 1); + DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx); + DetectEngineCtxFree(de_ctx); + AppLayerParserThreadCtxFree(alp_tctx); + StreamTcpFreeConfig(true); + FLOW_DESTROY(&f); + StatsThreadCleanup(&th_v.stats); + EngineModeSetIDS(); + + PASS; +} + /** \test a pending LTE rule the walk would discard must not hold the hook. * Pins the filters in DetectFwLteRuleStillLive(): a rule whose header and protocol * checks pass but whose packet engines fail is no barrier, as the full walk would @@ -4971,6 +5095,7 @@ void SigRegisterTests(void) UtRegisterTest("DetectFwLteCoverageTest01", DetectFwLteCoverageTest01); UtRegisterTest("DetectFwLteCoverageTest02", DetectFwLteCoverageTest02); UtRegisterTest("DetectFwLteCoverageTest03", DetectFwLteCoverageTest03); + UtRegisterTest("DetectFwLteMpmWalkTest01", DetectFwLteMpmWalkTest01); UtRegisterTest("DetectFwLtePendingLiveTest01", DetectFwLtePendingLiveTest01); UtRegisterTest("DetectFwLtePendingLivePolicyTest01", DetectFwLtePendingLivePolicyTest01);