• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.61
/src/detect-xbits.c
1
/* Copyright (C) 2007-2020 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
/**
19
 * \file
20
 *
21
 *  \author Victor Julien <victor@inliniac.net>
22
 *
23
 * Implements the xbits keyword
24
 */
25

26
#include "suricata-common.h"
27
#include "decode.h"
28
#include "action-globals.h"
29
#include "detect.h"
30
#include "threads.h"
31
#include "flow.h"
32
#include "flow-util.h"
33
#include "detect-xbits.h"
34
#include "detect-hostbits.h"
35
#include "util-spm.h"
36
#include "util-byte.h"
37

38
#include "detect-engine-sigorder.h"
39

40
#include "app-layer-parser.h"
41

42
#include "detect-parse.h"
43
#include "detect-engine.h"
44
#include "detect-engine-mpm.h"
45
#include "detect-engine-state.h"
46
#include "detect-engine-build.h"
47

48
#include "flow-bit.h"
49
#include "host-bit.h"
50
#include "ippair-bit.h"
51
#include "tx-bit.h"
52

53
#include "util-var-name.h"
54
#include "util-unittest.h"
55
#include "util-debug.h"
56

57
/*
58
    xbits:set,bitname,track ip_pair,expire 60
59
 */
60

61
static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
62
        void *txv, const Signature *s, const SigMatchCtx *ctx);
63
static int DetectXbitMatch (DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *);
64
static int DetectXbitSetup (DetectEngineCtx *, Signature *, const char *);
65
#ifdef UNITTESTS
66
static void XBitsRegisterTests(void);
67
#endif
68
static void DetectXbitFree (DetectEngineCtx *, void *);
69

70
void DetectXbitsRegister (void)
71
{
3✔
72
    sigmatch_table[DETECT_XBITS].name = "xbits";
3✔
73
    sigmatch_table[DETECT_XBITS].desc = "operate on bits";
3✔
74
    sigmatch_table[DETECT_XBITS].url = "/rules/xbits.html";
3✔
75
    sigmatch_table[DETECT_XBITS].AppLayerTxMatch = DetectXbitTxMatch;
3✔
76
    sigmatch_table[DETECT_XBITS].Match = DetectXbitMatch;
3✔
77
    sigmatch_table[DETECT_XBITS].Setup = DetectXbitSetup;
3✔
78
    sigmatch_table[DETECT_XBITS].Free  = DetectXbitFree;
3✔
79
#ifdef UNITTESTS
80
    sigmatch_table[DETECT_XBITS].RegisterTests = XBitsRegisterTests;
81
#endif
82
    /* this is compatible to ip-only signatures */
83
    sigmatch_table[DETECT_XBITS].flags |= (SIGMATCH_IPONLY_COMPAT | SIGMATCH_SUPPORT_FIREWALL);
3✔
84
}
3✔
85

86
static int DetectIPPairbitMatchToggle (Packet *p, const DetectXbitsData *fd)
87
{
×
88
    IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
×
89
    if (pair == NULL)
×
90
        return 0;
×
91

92
    IPPairBitToggle(pair, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
×
93
    IPPairRelease(pair);
×
94
    return 1;
×
95
}
×
96

97
/* return true even if bit not found */
98
static int DetectIPPairbitMatchUnset (Packet *p, const DetectXbitsData *fd)
99
{
×
100
    IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
×
101
    if (pair == NULL)
×
102
        return 1;
×
103

104
    IPPairBitUnset(pair,fd->idx);
×
105
    IPPairRelease(pair);
×
106
    return 1;
×
107
}
×
108

109
static int DetectIPPairbitMatchSet (Packet *p, const DetectXbitsData *fd)
110
{
156✔
111
    IPPair *pair = IPPairGetIPPairFromHash(&p->src, &p->dst);
156✔
112
    if (pair == NULL)
156✔
113
        return 0;
×
114

115
    IPPairBitSet(pair, fd->idx, SCTIME_ADD_SECS(p->ts, fd->expire));
156✔
116
    IPPairRelease(pair);
156✔
117
    return 1;
156✔
118
}
156✔
119

120
static int DetectIPPairbitMatchIsset (Packet *p, const DetectXbitsData *fd)
121
{
138✔
122
    int r = 0;
138✔
123
    IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
138✔
124
    if (pair == NULL)
138✔
125
        return 0;
28✔
126

127
    r = IPPairBitIsset(pair, fd->idx, p->ts);
110✔
128
    IPPairRelease(pair);
110✔
129
    return r;
110✔
130
}
138✔
131

132
static int DetectIPPairbitMatchIsnotset (Packet *p, const DetectXbitsData *fd)
133
{
×
134
    int r = 0;
×
135
    IPPair *pair = IPPairLookupIPPairFromHash(&p->src, &p->dst);
×
136
    if (pair == NULL)
×
137
        return 1;
×
138

139
    r = IPPairBitIsnotset(pair, fd->idx, p->ts);
×
140
    IPPairRelease(pair);
×
141
    return r;
×
142
}
×
143

144
static int DetectXbitMatchIPPair(Packet *p, const DetectXbitsData *xd)
145
{
294✔
146
    switch (xd->cmd) {
294✔
147
        case DETECT_XBITS_CMD_ISSET:
138✔
148
            return DetectIPPairbitMatchIsset(p,xd);
138✔
149
        case DETECT_XBITS_CMD_ISNOTSET:
×
150
            return DetectIPPairbitMatchIsnotset(p,xd);
×
151
        case DETECT_XBITS_CMD_SET:
156✔
152
            return DetectIPPairbitMatchSet(p,xd);
156✔
153
        case DETECT_XBITS_CMD_UNSET:
×
154
            return DetectIPPairbitMatchUnset(p,xd);
×
155
        case DETECT_XBITS_CMD_TOGGLE:
×
156
            return DetectIPPairbitMatchToggle(p,xd);
×
157
    }
294✔
158
    return 0;
×
159
}
294✔
160

161
static int DetectXbitPostMatchTx(
162
        DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const DetectXbitsData *xd)
UNCOV
163
{
×
UNCOV
164
    if (p->flow == NULL)
×
165
        return 0;
×
UNCOV
166
    if (!det_ctx->tx_id_set)
×
167
        return 0;
×
UNCOV
168
    Flow *f = p->flow;
×
UNCOV
169
    void *txv = AppLayerParserGetTx(f->proto, f->alproto, f->alstate, det_ctx->tx_id);
×
UNCOV
170
    if (txv == NULL)
×
171
        return 0;
×
UNCOV
172
    AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
×
173

UNCOV
174
    if (xd->cmd != DETECT_XBITS_CMD_SET)
×
175
        return 0;
×
176

UNCOV
177
    SCLogDebug("sid %u: post-match SET for bit %u on tx:%" PRIu64 ", txd:%p", s->id, xd->idx,
×
UNCOV
178
            det_ctx->tx_id, txd);
×
179

UNCOV
180
    return TxBitSet(txd, xd->idx);
×
UNCOV
181
}
×
182

183
/*
184
 * returns 0: no match
185
 *         1: match
186
 *        -1: error
187
 */
188

189
static int DetectXbitMatch (DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx)
190
{
295✔
191
    const DetectXbitsData *fd = (const DetectXbitsData *)ctx;
295✔
192
    if (fd == NULL)
295✔
193
        return 0;
×
194

195
    switch (fd->type) {
295✔
196
        case VAR_TYPE_HOST_BIT:
×
197
            return DetectXbitMatchHost(p, (const DetectXbitsData *)fd);
×
198
            break;
×
199
        case VAR_TYPE_IPPAIR_BIT:
294✔
200
            return DetectXbitMatchIPPair(p, (const DetectXbitsData *)fd);
294✔
201
            break;
×
UNCOV
202
        case VAR_TYPE_TX_BIT:
×
203
            // TODO this is for PostMatch only. Can we validate somehow?
UNCOV
204
            return DetectXbitPostMatchTx(det_ctx, p, s, fd);
×
205
            break;
×
206
        default:
1✔
207
            break;
1✔
208
    }
295✔
209
    return 0;
1✔
210
}
295✔
211

212
static int DetectXbitTxMatch(DetectEngineThreadCtx *det_ctx, Flow *f, uint8_t flags, void *state,
213
        void *txv, const Signature *s, const SigMatchCtx *ctx)
UNCOV
214
{
×
UNCOV
215
    const DetectXbitsData *xd = (const DetectXbitsData *)ctx;
×
UNCOV
216
    DEBUG_VALIDATE_BUG_ON(xd == NULL);
×
217

UNCOV
218
    AppLayerTxData *txd = AppLayerParserGetTxData(f->proto, f->alproto, txv);
×
219

UNCOV
220
    SCLogDebug("sid:%u: tx:%" PRIu64 ", txd->txbits:%p", s->id, det_ctx->tx_id, txd->txbits);
×
UNCOV
221
    int r = TxBitIsset(txd, xd->idx);
×
UNCOV
222
    if (r == 1) {
×
UNCOV
223
        return DETECT_ENGINE_INSPECT_SIG_MATCH;
×
UNCOV
224
    }
×
225
    return DETECT_ENGINE_INSPECT_SIG_NO_MATCH;
×
UNCOV
226
}
×
227

228
/** \internal
229
 *  \brief parse xbits rule options
230
 *  \retval 0 ok
231
 *  \retval -1 bad
232
 *  \param[out] cdout return DetectXbitsData structure or NULL if noalert
233
 */
234
static int DetectXbitParse(DetectEngineCtx *de_ctx,
235
        const char *rawstr, DetectXbitsData **cdout)
236
{
6,203✔
237
    bool cmd_set = false;
6,203✔
238
    bool name_set = false;
6,203✔
239
    bool track_set = false;
6,203✔
240
    bool expire_set = false;
6,203✔
241
    uint8_t cmd = 0;
6,203✔
242
    uint8_t track = 0;
6,203✔
243
    enum VarTypes var_type = VAR_TYPE_NOT_SET;
6,203✔
244
    uint32_t expire = DETECT_XBITS_EXPIRE_DEFAULT;
6,203✔
245
    DetectXbitsData *cd = NULL;
6,203✔
246
    char name[256] = "";
6,203✔
247
    char copy[strlen(rawstr) + 1];
6,203✔
248
    strlcpy(copy, rawstr, sizeof(copy));
6,203✔
249
    char *context = NULL;
6,203✔
250
    char *token = strtok_r(copy, ",", &context);
6,203✔
251
    while (token != NULL) {
21,248✔
252
        while (*token != '\0' && isblank(*token)) {
27,287✔
253
            token++;
9,503✔
254
        }
9,503✔
255
        char *val = strchr(token, ' ');
17,784✔
256
        if (val != NULL) {
17,784✔
257
            *val++ = '\0';
5,203✔
258
            while (*val != '\0' && isblank(*val)) {
5,649✔
259
                val++;
446✔
260
            }
446✔
261
        } else {
12,581✔
262
            SCLogDebug("val %s", token);
12,581✔
263
        }
12,581✔
264
        if (strlen(token) == 0) {
17,784✔
265
            goto next;
285✔
266
        }
285✔
267
        if (strcmp(token, "noalert") == 0 && !cmd_set) {
17,499✔
268
            if (strtok_r(NULL, ",", &context) != NULL) {
5✔
269
                return -1;
5✔
270
            }
5✔
UNCOV
271
            if (val && strlen(val) != 0) {
×
272
                return -1;
×
273
            }
×
UNCOV
274
            *cdout = NULL;
×
UNCOV
275
            return 0;
×
UNCOV
276
        }
×
277
        if (!cmd_set) {
17,494✔
278
            if (val && strlen(val) != 0) {
6,198✔
279
                return -1;
135✔
280
            }
135✔
281
            if (strcmp(token, "set") == 0) {
6,063✔
282
                cmd = DETECT_XBITS_CMD_SET;
1,179✔
283
            } else if (strcmp(token, "isset") == 0) {
4,884✔
284
                cmd = DETECT_XBITS_CMD_ISSET;
4,567✔
285
            } else if (strcmp(token, "unset") == 0) {
4,567✔
286
                cmd = DETECT_XBITS_CMD_UNSET;
67✔
287
            } else if (strcmp(token, "isnotset") == 0) {
250✔
288
                cmd = DETECT_XBITS_CMD_ISNOTSET;
2✔
289
            } else if (strcmp(token, "toggle") == 0) {
248✔
290
                cmd = DETECT_XBITS_CMD_TOGGLE;
17✔
291
            } else {
231✔
292
                SCLogError("Invalid xbits cmd: %s", token);
231✔
293
                return -1;
231✔
294
            }
231✔
295
            cmd_set = true;
5,832✔
296
        } else if (!name_set) {
11,296✔
297
            if (val && strlen(val) != 0) {
5,832✔
298
                return -1;
835✔
299
            }
835✔
300
            strlcpy(name, token, sizeof(name));
4,997✔
301
            name_set = true;
4,997✔
302
        } else if (!track_set || !expire_set) {
5,464✔
303
            if (val == NULL) {
5,464✔
304
                return -1;
1,231✔
305
            }
1,231✔
306
            if (strcmp(token, "track") == 0) {
4,233✔
307
                if (track_set) {
3,125✔
308
                    return -1;
×
309
                }
×
310
                if (strcmp(val, "ip_src") == 0) {
3,125✔
UNCOV
311
                    track = DETECT_XBITS_TRACK_IPSRC;
×
UNCOV
312
                    var_type = VAR_TYPE_HOST_BIT;
×
313
                } else if (strcmp(val, "ip_dst") == 0) {
3,125✔
314
                    track = DETECT_XBITS_TRACK_IPDST;
34✔
315
                    var_type = VAR_TYPE_HOST_BIT;
34✔
316
                } else if (strcmp(val, "ip_pair") == 0) {
3,091✔
317
                    track = DETECT_XBITS_TRACK_IPPAIR;
2,794✔
318
                    var_type = VAR_TYPE_IPPAIR_BIT;
2,794✔
319
                } else if (strcmp(val, "tx") == 0) {
2,794✔
UNCOV
320
                    track = DETECT_XBITS_TRACK_TX;
×
UNCOV
321
                    var_type = VAR_TYPE_TX_BIT;
×
322
                } else {
297✔
323
                    SCLogError("Invalid xbits tracker: %s", val);
297✔
324
                    return -1;
297✔
325
                }
297✔
326
                track_set = true;
2,828✔
327
            } else if (strcmp(token, "expire") == 0) {
2,828✔
328
                if (expire_set) {
145✔
329
                    return -1;
×
330
                }
×
331
                if ((StringParseUint32(&expire, 10, 0, val) < 0) || (expire == 0)) {
145✔
332
                    SCLogError("Invalid expire value: %s", val);
5✔
333
                    return -1;
5✔
334
                }
5✔
335
                expire_set = true;
140✔
336
            }
140✔
337
        } else {
4,233✔
338
            SCLogError("Invalid xbits keyword: %s", token);
×
339
            return -1;
×
340
        }
×
341
    next:
15,045✔
342
        token = strtok_r(NULL, ",", &context);
15,045✔
343
    }
15,045✔
344

345
    if (track == DETECT_XBITS_TRACK_TX) {
3,464✔
UNCOV
346
        if (cmd != DETECT_XBITS_CMD_ISSET && cmd != DETECT_XBITS_CMD_SET) {
×
347
            SCLogError("tx xbits only support set and isset");
×
348
            return -1;
×
349
        }
×
UNCOV
350
    }
×
351

352
    cd = SCCalloc(1, sizeof(DetectXbitsData));
3,464✔
353
    if (unlikely(cd == NULL))
3,464✔
354
        return -1;
×
355

356
    uint32_t varname_id = VarNameStoreRegister(name, var_type);
3,464✔
357
    if (unlikely(varname_id == 0)) {
3,464✔
358
        SCFree(cd);
×
359
        return -1;
×
360
    }
×
361
    cd->idx = varname_id;
3,464✔
362
    cd->cmd = cmd;
3,464✔
363
    cd->tracker = track;
3,464✔
364
    cd->type = var_type;
3,464✔
365
    cd->expire = expire;
3,464✔
366

367
    SCLogDebug("idx %" PRIu32 ", cmd %d, name %s", cd->idx, cmd, strlen(name) ? name : "(none)");
3,464✔
368

369
    *cdout = cd;
3,464✔
370
    return 0;
3,464✔
371
}
3,464✔
372

373
int DetectXbitSetup (DetectEngineCtx *de_ctx, Signature *s, const char *rawstr)
374
{
6,203✔
375
    DetectXbitsData *cd = NULL;
6,203✔
376

377
    int result = DetectXbitParse(de_ctx, rawstr, &cd);
6,203✔
378
    if (result < 0) {
6,203✔
379
        return -1;
2,739✔
380
    } else if (cd == NULL) {
3,464✔
381
        /* noalert doesn't use a cd/sm struct. It flags the sig. We're done. */
UNCOV
382
        s->action &= ~ACTION_ALERT;
×
UNCOV
383
        return 0;
×
UNCOV
384
    }
×
385

386
    /* Okay so far so good, lets get this into a SigMatch
387
     * and put it in the Signature. */
388
    switch (cd->cmd) {
3,464✔
389
        /* case DETECT_XBITS_CMD_NOALERT can't happen here */
390
        case DETECT_XBITS_CMD_ISNOTSET:
2✔
391
        case DETECT_XBITS_CMD_ISSET: {
3,115✔
392
            int list = DETECT_SM_LIST_MATCH;
3,115✔
393
            if (cd->tracker == DETECT_XBITS_TRACK_TX) {
3,115✔
UNCOV
394
                SCLogDebug("tx xbit isset");
×
UNCOV
395
                if (s->init_data->hook.type != SIGNATURE_HOOK_TYPE_APP) {
×
396
                    SCLogError("tx xbits require an explicit rule hook");
×
397
                    goto error;
×
398
                }
×
UNCOV
399
                list = s->init_data->hook.sm_list;
×
UNCOV
400
                SCLogDebug("setting list %d", list);
×
401

UNCOV
402
                if (list == -1) {
×
403
                    SCLogError("tx xbits failed to set up"); // TODO how would we get here?
×
404
                    goto error;
×
405
                }
×
UNCOV
406
            }
×
407

408
            SCLogDebug("adding match/txmatch");
3,115✔
409
            /* checks, so packet list */
410
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd, list) ==
3,115✔
411
                    NULL) {
3,115✔
412
                goto error;
×
413
            }
×
414
            break;
3,115✔
415
        }
3,115✔
416
        // all other cases
417
        // DETECT_XBITS_CMD_SET, DETECT_XBITS_CMD_UNSET, DETECT_XBITS_CMD_TOGGLE:
418
        default:
3,115✔
419
            SCLogDebug("adding post-match");
349✔
420
            /* modifiers, only run when entire sig has matched */
421
            if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_XBITS, (SigMatchCtx *)cd,
349✔
422
                        DETECT_SM_LIST_POSTMATCH) == NULL) {
349✔
423
                goto error;
×
424
            }
×
425
            break;
349✔
426
    }
3,464✔
427

428
    return 0;
3,464✔
429

430
error:
×
431
    DetectXbitFree(de_ctx, cd);
×
432
    return -1;
×
433
}
3,464✔
434

435
static void DetectXbitFree (DetectEngineCtx *de_ctx, void *ptr)
436
{
3,464✔
437
    if (ptr == NULL)
3,464✔
438
        return;
×
439

440
    DetectXbitsData *fd = (DetectXbitsData *)ptr;
3,464✔
441
    VarNameStoreUnregister(fd->idx, fd->type);
3,464✔
442

443
    SCFree(fd);
3,464✔
444
}
3,464✔
445

446
#ifdef UNITTESTS
447

448
static void XBitsTestSetup(void)
449
{
450
    StorageCleanup();
451
    StorageInit();
452
    HostBitInitCtx();
453
    IPPairBitInitCtx();
454
    StorageFinalize();
455
    HostInitConfig(true);
456
    IPPairInitConfig(true);
457
}
458

459
static void XBitsTestShutdown(void)
460
{
461
    HostShutdown();
462
    IPPairShutdown();
463
    StorageCleanup();
464
}
465

466

467
static int XBitsTestParse01(void)
468
{
469
    DetectEngineCtx *de_ctx = NULL;
470
    de_ctx = DetectEngineCtxInit();
471
    FAIL_IF_NULL(de_ctx);
472
    de_ctx->flags |= DE_QUIET;
473
    DetectXbitsData *cd = NULL;
474

475
#define BAD_INPUT(str) \
476
    FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == -1);
477

478
    BAD_INPUT("alert");
479
    BAD_INPUT("n0alert");
480
    BAD_INPUT("nOalert");
481
    BAD_INPUT("set,abc,track nonsense, expire 3600");
482
    BAD_INPUT("set,abc,track ip_source, expire 3600");
483
    BAD_INPUT("set,abc,track ip_src, expire -1");
484
    BAD_INPUT("set,abc,track ip_src, expire 0");
485

486
#undef BAD_INPUT
487

488
#define GOOD_INPUT(str, command, trk, typ, exp)             \
489
    FAIL_IF_NOT(DetectXbitParse(de_ctx, (str), &cd) == 0);  \
490
    FAIL_IF_NULL(cd);                                       \
491
    FAIL_IF_NOT(cd->cmd == (command));                      \
492
    FAIL_IF_NOT(cd->tracker == (trk));                      \
493
    FAIL_IF_NOT(cd->type == (typ));                         \
494
    FAIL_IF_NOT(cd->expire == (exp));                       \
495
    DetectXbitFree(NULL, cd);                               \
496
    cd = NULL;
497

498
    GOOD_INPUT("set,abc,track ip_pair",
499
            DETECT_XBITS_CMD_SET,
500
            DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT,
501
            DETECT_XBITS_EXPIRE_DEFAULT);
502
    GOOD_INPUT("set,abc,track ip_pair, expire 3600",
503
            DETECT_XBITS_CMD_SET,
504
            DETECT_XBITS_TRACK_IPPAIR, VAR_TYPE_IPPAIR_BIT,
505
            3600);
506
    GOOD_INPUT("set,abc,track ip_src, expire 1234",
507
            DETECT_XBITS_CMD_SET,
508
            DETECT_XBITS_TRACK_IPSRC, VAR_TYPE_HOST_BIT,
509
            1234);
510

511
#undef GOOD_INPUT
512

513
    DetectEngineCtxFree(de_ctx);
514
    PASS;
515
}
516

517
/**
518
 * \test
519
 */
520

521
static int XBitsTestSig01(void)
522
{
523
    uint8_t *buf = (uint8_t *)
524
                    "GET /one/ HTTP/1.1\r\n"
525
                    "Host: one.example.org\r\n"
526
                    "\r\n";
527
    uint16_t buflen = strlen((char *)buf);
528
    Packet *p = PacketGetFromAlloc();
529
    FAIL_IF_NULL(p);
530
    ThreadVars th_v;
531
    DetectEngineThreadCtx *det_ctx = NULL;
532
    DetectEngineCtx *de_ctx = NULL;
533

534
    memset(&th_v, 0, sizeof(th_v));
535
    StatsThreadInit(&th_v.stats);
536
    p->src.family = AF_INET;
537
    p->dst.family = AF_INET;
538
    p->payload = buf;
539
    p->payload_len = buflen;
540
    p->proto = IPPROTO_TCP;
541

542
    XBitsTestSetup();
543

544
    de_ctx = DetectEngineCtxInit();
545
    FAIL_IF_NULL(de_ctx);
546
    de_ctx->flags |= DE_QUIET;
547

548
    Signature *s = DetectEngineAppendSig(de_ctx,
549
            "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:1;)");
550
    FAIL_IF_NULL(s);
551

552
    SigGroupBuild(de_ctx);
553
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
554
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
555
    DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
556
    DetectEngineCtxFree(de_ctx);
557
    XBitsTestShutdown();
558
    PacketFree(p);
559
    StatsThreadCleanup(&th_v.stats);
560
    StatsReleaseResources();
561
    PASS;
562
}
563

564
/**
565
 * \test various options
566
 *
567
 *  \retval 1 on success
568
 *  \retval 0 on failure
569
 */
570

571
static int XBitsTestSig02(void)
572
{
573
    Signature *s = NULL;
574
    DetectEngineCtx *de_ctx = NULL;
575
    de_ctx = DetectEngineCtxInit();
576
    FAIL_IF_NULL(de_ctx);
577
    de_ctx->flags |= DE_QUIET;
578

579
    s = DetectEngineAppendSig(de_ctx,
580
            "alert ip any any -> any any (xbits:isset,abc,track ip_src; content:\"GET \"; sid:1;)");
581
    FAIL_IF_NULL(s);
582

583
    s = DetectEngineAppendSig(de_ctx,
584
            "alert ip any any -> any any (xbits:isnotset,abc,track ip_dst; content:\"GET \"; sid:2;)");
585
    FAIL_IF_NULL(s);
586

587
    s = DetectEngineAppendSig(de_ctx,
588
            "alert ip any any -> any any (xbits:set,abc,track ip_pair; content:\"GET \"; sid:3;)");
589
    FAIL_IF_NULL(s);
590

591
    s = DetectEngineAppendSig(de_ctx,
592
            "alert ip any any -> any any (xbits:unset,abc,track ip_src; content:\"GET \"; sid:4;)");
593
    FAIL_IF_NULL(s);
594

595
    s = DetectEngineAppendSig(de_ctx,
596
            "alert ip any any -> any any (xbits:toggle,abc,track ip_dst; content:\"GET \"; sid:5;)");
597
    FAIL_IF_NULL(s);
598

599
    s = DetectEngineAppendSig(de_ctx,
600
            "alert ip any any -> any any (xbits:!set,abc,track ip_dst; content:\"GET \"; sid:6;)");
601
    FAIL_IF_NOT_NULL(s);
602

603
    DetectEngineCtxFree(de_ctx);
604
    PASS;
605
}
606

607
/* Test to demonstrate redmine bug 4820 */
608
static int XBitsTestSig03(void)
609
{
610
    DetectEngineCtx *de_ctx = NULL;
611
    XBitsTestSetup();
612
    de_ctx = DetectEngineCtxInit();
613
    FAIL_IF_NULL(de_ctx);
614
    de_ctx->flags |= DE_QUIET;
615

616
    Signature *s = DetectEngineAppendSig(
617
            de_ctx, "alert http any any -> any any (msg:\"TEST - No Error\")\";\
618
            flow:established,to_server; http.method; content:\"GET\"; \
619
            xbits:set,ET.2020_8260.1,track ip_src,expire 10; sid:1;)");
620
    FAIL_IF_NULL(s);
621

622
    DetectEngineCtxFree(de_ctx);
623
    XBitsTestShutdown();
624
    PASS;
625
}
626

627
/* Test to demonstrate redmine bug 4820 */
628
static int XBitsTestSig04(void)
629
{
630
    DetectEngineCtx *de_ctx = NULL;
631
    XBitsTestSetup();
632
    de_ctx = DetectEngineCtxInit();
633
    FAIL_IF_NULL(de_ctx);
634
    de_ctx->flags |= DE_QUIET;
635

636
    Signature *s =
637
            DetectEngineAppendSig(de_ctx, "alert http any any -> any any (msg:\"TEST - Error\")\"; \
638
            flow:established,to_server; http.method; content:\"GET\"; \
639
            xbits:set,ET.2020_8260.1,noalert,track ip_src,expire 10; sid:2;)");
640
    FAIL_IF_NOT_NULL(s);
641

642
    DetectEngineCtxFree(de_ctx);
643
    XBitsTestShutdown();
644
    PASS;
645
}
646

647
static int XBitsTestSig05(void)
648
{
649
    DetectEngineCtx *de_ctx = NULL;
650
    XBitsTestSetup();
651
    de_ctx = DetectEngineCtxInit();
652
    FAIL_IF_NULL(de_ctx);
653
    de_ctx->flags |= DE_QUIET;
654

655
    Signature *s = DetectEngineAppendSig(de_ctx,
656
            "alert http any any -> any any (msg:\"ET EXPLOIT Possible Pulse Secure VPN RCE "
657
            "Chain Stage 1 Inbound - Request Config Backup (CVE-2020-8260)\"; "
658
            "flow:established,to_server; http.method; content:\"GET\"; http.uri; "
659
            "content:\"/dana-admin/cached/config/config.cgi?type=system\"; fast_pattern; "
660
            "xbits:set,ET.2020_8260.1,track ip_src,expire 10; xbits:noalert; "
661
            "classtype:attempted-admin; sid:2033750; rev:1;");
662
    FAIL_IF_NULL(s);
663

664
    DetectEngineCtxFree(de_ctx);
665
    XBitsTestShutdown();
666
    PASS;
667
}
668

669
static int XBitsTestSig06(void)
670
{
671
    DetectEngineCtx *de_ctx = NULL;
672
    XBitsTestSetup();
673
    de_ctx = DetectEngineCtxInit();
674
    FAIL_IF_NULL(de_ctx);
675
    de_ctx->flags |= DE_QUIET;
676

677
    Signature *s = DetectEngineAppendSig(de_ctx,
678
            "alert http any any -> any any (msg:\"ET EXPLOIT Possible Pulse Secure VPN RCE "
679
            "Chain Stage 2 Inbound - Upload Malicious Config (CVE-2020-8260)\"; "
680
            "flow:established,to_server; http.method; content:\"POST\"; http.uri; "
681
            "content:\"/dana-admin/cached/config/import.cgi\"; "
682
            "xbits:isset,ET.2020_8260.1,track ip_src,expire 10;"
683
            "xbits:set,ET.2020_8260.2,track ip_src,expire 10; "
684
            "classtype:attempted-admin; sid:2033751; rev:1;");
685
    FAIL_IF_NULL(s);
686

687
    DetectEngineCtxFree(de_ctx);
688
    XBitsTestShutdown();
689
    PASS;
690
}
691

692
static int DetectXBitsTestBadRules(void)
693
{
694
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
695
    FAIL_IF_NULL(de_ctx);
696

697
    const char *sigs[] = {
698
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit1,noalert,track "
699
        "ip_src;sid:1;)",
700
        "alert http any any -> any any (content:\"abc\"; xbits:noalert,set,bit1,noalert,track "
701
        "ip_src;sid:10;)",
702
        "alert http any any -> any any (content:\"abc\"; xbits:isset,bit2,track "
703
        "ip_dst,asdf;sid:2;)",
704
        "alert http any any -> any any (content:\"abc\"; xbits:isnotset,track ip_pair;sid:3;)",
705
        "alert http any any -> any any (content:\"abc\"; xbits:toggle,track ip_pair,bit4;sid:4;)",
706
        "alert http any any -> any any (content:\"abc\"; xbits:unset,bit5,track ipsrc;sid:5;)",
707
        "alert http any any -> any any (content:\"abc\"; xbits:bit6,set,track ip_src,expire "
708
        "10;sid:6;)",
709
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit7,track "
710
        "ip_pair,expire;sid:7;)",
711
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit7,trackk ip_pair,expire "
712
        "3600, noalert;sid:8;)",
713
        NULL,
714
    };
715

716
    const char **sig = sigs;
717
    while (*sig) {
718
        SCLogDebug("sig %s", *sig);
719
        Signature *s = DetectEngineAppendSig(de_ctx, *sig);
720
        FAIL_IF_NOT_NULL(s);
721
        sig++;
722
    }
723

724
    DetectEngineCtxFree(de_ctx);
725
    PASS;
726
}
727

728
static int DetectXBitsTestGoodRules(void)
729
{
730
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
731
    FAIL_IF_NULL(de_ctx);
732

733
    const char *sigs[] = {
734
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit1,track ip_src;sid:1;)",
735
        "alert http any any -> any any (content:\"abc\"; xbits:isset,bit2,track ip_dst;sid:2;)",
736
        "alert http any any -> any any (content:\"abc\"; xbits:isnotset, bit3,  track "
737
        "ip_pair;sid:3;)",
738
        "alert http any any -> any any (content:\"abc\"; xbits:toggle,bit4, track   "
739
        "ip_pair;sid:4;)",
740
        "alert http any any -> any any (content:\"abc\"; xbits:  unset ,bit5,track ip_src;sid:5;)",
741
        "alert http any any -> any any (content:\"abc\"; xbits:set,bit6 ,track ip_src, expire "
742
        "10 ;sid:6;)",
743
        "alert http any any -> any any (content:\"abc\"; xbits:set, bit7, track ip_pair, expire "
744
        "3600;sid:7;)",
745
        "alert http any any -> any any (content:\"abc\"; xbits:set, bit7, track ip_pair, expire "
746
        "3600; xbits:noalert; sid:8;)",
747
        "alert http any any -> any any (content:\"abc\"; xbits:noalert; xbits:set, bit7, track "
748
        "ip_pair, expire "
749
        "3600;sid:9;)",
750
        NULL,
751
    };
752

753
    const char **sig = sigs;
754
    while (*sig) {
755
        SCLogDebug("sig %s", *sig);
756
        Signature *s = DetectEngineAppendSig(de_ctx, *sig);
757
        FAIL_IF_NULL(s);
758
        sig++;
759
    }
760

761
    DetectEngineCtxFree(de_ctx);
762
    PASS;
763
}
764

765
/**
766
 * \brief this function registers unit tests for XBits
767
 */
768
static void XBitsRegisterTests(void)
769
{
770
    UtRegisterTest("XBitsTestParse01", XBitsTestParse01);
771
    UtRegisterTest("XBitsTestSig01", XBitsTestSig01);
772
    UtRegisterTest("XBitsTestSig02", XBitsTestSig02);
773
    UtRegisterTest("XBitsTestSig03", XBitsTestSig03);
774
    UtRegisterTest("XBitsTestSig04", XBitsTestSig04);
775
    UtRegisterTest("XBitsTestSig05", XBitsTestSig05);
776
    UtRegisterTest("XBitsTestSig06", XBitsTestSig06);
777
    UtRegisterTest("DetectXBitsTestBadRules", DetectXBitsTestBadRules);
778
    UtRegisterTest("DetectXBitsTestGoodRules", DetectXBitsTestGoodRules);
779
}
780
#endif /* UNITTESTS */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc