• 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

89.05
/src/detect-isdataat.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 Pablo Rincon <pablo.rincon.crespo@gmail.com>
22
 *
23
 * Implements isdataat keyword
24
 */
25

26
#include "suricata-common.h"
27
#include "decode.h"
28
#include "detect.h"
29
#include "detect-engine.h"
30
#include "detect-engine-buffer.h"
31
#include "detect-parse.h"
32
#include "app-layer.h"
33

34
#include "util-unittest.h"
35
#include "util-unittest-helper.h"
36

37
#include "detect-isdataat.h"
38
#include "detect-content.h"
39
#include "detect-bytetest.h"
40
#include "detect-uricontent.h"
41
#include "detect-engine-build.h"
42

43
#include "flow.h"
44
#include "flow-var.h"
45

46
#include "util-debug.h"
47
#include "util-byte.h"
48
#include "detect-pcre.h"
49
#include "detect-byte.h"
50

51
/**
52
 * \brief Regex for parsing our isdataat options
53
 */
54
#define PARSE_REGEX  "^\\s*!?([^\\s,]+)\\s*(,\\s*relative)?\\s*(,\\s*rawbytes\\s*)?\\s*$"
3✔
55

56
static DetectParseRegex parse_regex;
57

58
int DetectIsdataatSetup (DetectEngineCtx *, Signature *, const char *);
59
#ifdef UNITTESTS
60
static void DetectIsdataatRegisterTests(void);
61
static void DetectAbsentRegisterTests(void);
62
#endif
63
void DetectIsdataatFree(DetectEngineCtx *, void *);
64

65
static int DetectEndsWithSetup (DetectEngineCtx *de_ctx, Signature *s, const char *nullstr);
66

67
static void DetectAbsentFree(DetectEngineCtx *de_ctx, void *ptr)
68
{
441✔
69
    SCFree(ptr);
441✔
70
}
441✔
71

72
static int DetectAbsentSetup(DetectEngineCtx *de_ctx, Signature *s, const char *optstr)
73
{
503✔
74
    if (s->init_data->list == DETECT_SM_LIST_NOTSET) {
503✔
75
        SCLogError("no buffer for absent keyword");
1✔
76
        return -1;
1✔
77
    }
1✔
78

79
    if (DetectBufferGetActiveList(de_ctx, s) == -1)
502✔
80
        return -1;
×
81

82
    bool or_else;
502✔
83
    if (optstr == NULL) {
502✔
84
        or_else = false;
266✔
85
    } else if (strcmp(optstr, "or_else") == 0) {
266✔
86
        or_else = true;
179✔
87
    } else {
179✔
88
        SCLogError("unhandled value for absent keyword: %s", optstr);
57✔
89
        return -1;
57✔
90
    }
57✔
91
    if (s->init_data->curbuf == NULL || s->init_data->list != (int)s->init_data->curbuf->id) {
445✔
92
        SCLogError("unspected buffer for absent keyword");
2✔
93
        return -1;
2✔
94
    }
2✔
95
    const DetectBufferType *b = DetectEngineBufferTypeGetById(de_ctx, s->init_data->list);
443✔
96
    if (!b || b->frame) {
443✔
97
        SCLogError("absent does not work with frames");
1✔
98
        return -1;
1✔
99
    }
1✔
100
    if (s->init_data->curbuf->tail != NULL) {
442✔
101
        SCLogError("absent must come first right after buffer");
1✔
102
        return -1;
1✔
103
    }
1✔
104
    DetectAbsentData *dad = SCMalloc(sizeof(DetectAbsentData));
441✔
105
    if (unlikely(dad == NULL))
441✔
106
        return -1;
×
107

108
    dad->or_else = or_else;
441✔
109

110
    if (SCSigMatchAppendSMToList(
441✔
111
                de_ctx, s, DETECT_ABSENT, (SigMatchCtx *)dad, s->init_data->list) == NULL) {
441✔
112
        DetectAbsentFree(de_ctx, dad);
×
113
        return -1;
×
114
    }
×
115
    return 0;
441✔
116
}
441✔
117

118
bool DetectAbsentValidateContentCallback(const Signature *s, const SignatureInitDataBuffer *b)
119
{
580,652✔
120
    bool has_other = false;
580,652✔
121
    bool only_absent = false;
580,652✔
122
    bool has_absent = false;
580,652✔
123
    for (const SigMatch *sm = b->head; sm != NULL; sm = sm->next) {
1,283,003✔
124
        if (sm->type == DETECT_ABSENT) {
702,353✔
125
            has_absent = true;
322✔
126
            const DetectAbsentData *dad = (const DetectAbsentData *)sm->ctx;
322✔
127
            if (!dad->or_else) {
322✔
128
                only_absent = true;
147✔
129
            }
147✔
130
        } else {
702,031✔
131
            has_other = true;
702,031✔
132
            if (sm->type == DETECT_CONTENT) {
702,031✔
133
                const DetectContentData *cd = (DetectContentData *)sm->ctx;
226,203✔
134
                if (has_absent && (cd->flags & DETECT_CONTENT_FAST_PATTERN)) {
226,203✔
135
                    SCLogError("signature can't have absent and fast_pattern on the same buffer");
2✔
136
                    return false;
2✔
137
                }
2✔
138
            }
226,203✔
139
        }
702,031✔
140
    }
702,353✔
141

142
    if (only_absent && has_other) {
580,650✔
143
        SCLogError("signature can't have a buffer tested absent and tested with other keywords "
11✔
144
                   "such as content");
11✔
145
        return false;
11✔
146
    } else if (has_absent && !only_absent && !has_other) {
580,639✔
147
        SCLogError(
×
148
                "signature with absent: or_else expects other keywords to test on such as content");
×
149
        return false;
×
150
    }
×
151
    return true;
580,639✔
152
}
580,650✔
153

154
/**
155
 * \brief Registration function for isdataat: keyword
156
 */
157
void DetectIsdataatRegister(void)
158
{
3✔
159
    sigmatch_table[DETECT_ISDATAAT].name = "isdataat";
3✔
160
    sigmatch_table[DETECT_ISDATAAT].desc = "check if there is still data at a specific part of the payload";
3✔
161
    sigmatch_table[DETECT_ISDATAAT].url = "/rules/payload-keywords.html#isdataat";
3✔
162
    /* match is handled in DetectEngineContentInspection() */
163
    sigmatch_table[DETECT_ISDATAAT].Match = NULL;
3✔
164
    sigmatch_table[DETECT_ISDATAAT].Setup = DetectIsdataatSetup;
3✔
165
    sigmatch_table[DETECT_ISDATAAT].Free  = DetectIsdataatFree;
3✔
166
#ifdef UNITTESTS
167
    sigmatch_table[DETECT_ISDATAAT].RegisterTests = DetectIsdataatRegisterTests;
168
#endif
169
    sigmatch_table[DETECT_ENDS_WITH].name = "endswith";
3✔
170
    sigmatch_table[DETECT_ENDS_WITH].desc = "make sure the previous content matches exactly at the end of the buffer";
3✔
171
    sigmatch_table[DETECT_ENDS_WITH].url = "/rules/payload-keywords.html#endswith";
3✔
172
    sigmatch_table[DETECT_ENDS_WITH].Setup = DetectEndsWithSetup;
3✔
173
    sigmatch_table[DETECT_ENDS_WITH].flags = SIGMATCH_NOOPT;
3✔
174

175
    sigmatch_table[DETECT_ABSENT].name = "absent";
3✔
176
    sigmatch_table[DETECT_ABSENT].desc = "test if the buffer is absent";
3✔
177
    sigmatch_table[DETECT_ABSENT].url = "/rules/payload-keywords.html#absent";
3✔
178
    sigmatch_table[DETECT_ABSENT].Setup = DetectAbsentSetup;
3✔
179
    sigmatch_table[DETECT_ABSENT].Free = DetectAbsentFree;
3✔
180
    sigmatch_table[DETECT_ABSENT].flags = SIGMATCH_OPTIONAL_OPT;
3✔
181
#ifdef UNITTESTS
182
    sigmatch_table[DETECT_ABSENT].RegisterTests = DetectAbsentRegisterTests;
183
#endif
184

185
    DetectSetupParseRegexes(PARSE_REGEX, &parse_regex);
3✔
186
}
3✔
187

188
/**
189
 * \brief This function is used to parse isdataat options passed via isdataat: keyword
190
 *
191
 * \param de_ctx Pointer to the detection engine context
192
 * \param isdataatstr Pointer to the user provided isdataat options
193
 *
194
 * \retval idad pointer to DetectIsdataatData on success
195
 * \retval NULL on failure
196
 */
197
static DetectIsdataatData *DetectIsdataatParse (DetectEngineCtx *de_ctx, const char *isdataatstr, char **offset)
198
{
4,430✔
199
    DetectIsdataatData *idad = NULL;
4,430✔
200
    char *args[3] = {NULL,NULL,NULL};
4,430✔
201
    int res = 0;
4,430✔
202
    size_t pcre2_len;
4,430✔
203
    int i=0;
4,430✔
204

205
    pcre2_match_data *match = NULL;
4,430✔
206
    int ret = DetectParsePcreExec(&parse_regex, &match, isdataatstr, 0, 0);
4,430✔
207
    if (ret < 1 || ret > 4) {
4,430✔
208
        SCLogError("pcre_exec parse error, ret %" PRId32 ", string %s", ret, isdataatstr);
1,034✔
209
        goto error;
1,034✔
210
    }
1,034✔
211

212
    if (ret > 1) {
3,396✔
213
        const char *str_ptr;
3,396✔
214
        res = pcre2_substring_get_bynumber(match, 1, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
3,396✔
215
        if (res < 0) {
3,396✔
216
            SCLogError("pcre2_substring_get_bynumber failed");
×
217
            goto error;
×
218
        }
×
219
        args[0] = (char *)str_ptr;
3,396✔
220

221

222
        if (ret > 2) {
3,396✔
223
            res = pcre2_substring_get_bynumber(match, 2, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
2,435✔
224
            if (res < 0) {
2,435✔
225
                SCLogError("pcre2_substring_get_bynumber failed");
1✔
226
                goto error;
1✔
227
            }
1✔
228
            args[1] = (char *)str_ptr;
2,434✔
229
        }
2,434✔
230
        if (ret > 3) {
3,395✔
UNCOV
231
            res = pcre2_substring_get_bynumber(match, 3, (PCRE2_UCHAR8 **)&str_ptr, &pcre2_len);
×
UNCOV
232
            if (res < 0) {
×
233
                SCLogError("pcre2_substring_get_bynumber failed");
×
234
                goto error;
×
235
            }
×
UNCOV
236
            args[2] = (char *)str_ptr;
×
UNCOV
237
        }
×
238

239
        idad = SCMalloc(sizeof(DetectIsdataatData));
3,395✔
240
        if (unlikely(idad == NULL))
3,395✔
241
            goto error;
×
242

243
        idad->flags = 0;
3,395✔
244
        idad->dataat = 0;
3,395✔
245

246
        if (args[0][0] != '-' && isalpha((unsigned char)args[0][0])) {
3,395✔
247
            if (offset == NULL) {
212✔
248
                SCLogError("isdataat supplied with "
×
249
                           "var name for offset.  \"offset\" argument supplied to "
×
250
                           "this function has to be non-NULL");
×
251
                goto error;
×
252
            }
×
253
            *offset = SCStrdup(args[0]);
212✔
254
            if (*offset == NULL)
212✔
255
                goto error;
×
256
        } else {
3,183✔
257
            if (StringParseUint16(&idad->dataat, 10,
3,183✔
258
                                        strlen(args[0]), args[0]) < 0 ) {
3,183✔
259
                SCLogError("isdataat out of range");
340✔
260
                SCFree(idad);
340✔
261
                idad = NULL;
340✔
262
                goto error;
340✔
263
            }
340✔
264
        }
3,183✔
265

266
        if (args[1] !=NULL) {
3,055✔
267
            idad->flags |= ISDATAAT_RELATIVE;
2,366✔
268

269
            if(args[2] !=NULL)
2,366✔
UNCOV
270
                idad->flags |= ISDATAAT_RAWBYTES;
×
271
        }
2,366✔
272

273
        if (isdataatstr[0] == '!') {
3,055✔
274
            idad->flags |= ISDATAAT_NEGATED;
911✔
275
        }
911✔
276

277
        for (i = 0; i < (ret -1); i++) {
8,476✔
278
            if (args[i] != NULL)
5,421✔
279
                pcre2_substring_free((PCRE2_UCHAR8 *)args[i]);
5,421✔
280
        }
5,421✔
281

282
        pcre2_match_data_free(match);
3,055✔
283
        return idad;
3,055✔
284

285
    }
3,395✔
286

287
error:
1,375✔
288
    if (match) {
1,375✔
289
        pcre2_match_data_free(match);
1,375✔
290
    }
1,375✔
291
    for (i = 0; i < (ret -1) && i < 3; i++){
1,786✔
292
        if (args[i] != NULL)
411✔
293
            pcre2_substring_free((PCRE2_UCHAR8 *)args[i]);
411✔
294
    }
411✔
295

296
    if (idad != NULL)
1,375✔
297
        DetectIsdataatFree(de_ctx, idad);
×
298
    return NULL;
1,375✔
299

300
}
3,396✔
301

302
/**
303
 * \brief This function is used to add the parsed isdataatdata into the current
304
 *        signature.
305
 * \param de_ctx pointer to the Detection Engine Context
306
 * \param s pointer to the Current Signature
307
 * \param isdataatstr pointer to the user provided isdataat options
308
 *
309
 * \retval 0 on Success
310
 * \retval -1 on Failure
311
 */
312
int DetectIsdataatSetup (DetectEngineCtx *de_ctx, Signature *s, const char *isdataatstr)
313
{
4,430✔
314
    SigMatch *prev_pm = NULL;
4,430✔
315
    DetectIsdataatData *idad = NULL;
4,430✔
316
    char *offset = NULL;
4,430✔
317
    int ret = -1;
4,430✔
318

319
    idad = DetectIsdataatParse(de_ctx, isdataatstr, &offset);
4,430✔
320
    if (idad == NULL)
4,430✔
321
        return -1;
1,375✔
322

323
    int sm_list;
3,055✔
324
    if (s->init_data->list != DETECT_SM_LIST_NOTSET) {
3,055✔
325
        if (DetectBufferGetActiveList(de_ctx, s) == -1)
1,340✔
326
            goto end;
1✔
327
        sm_list = s->init_data->list;
1,339✔
328

329
        if (idad->flags & ISDATAAT_RELATIVE) {
1,339✔
330
            prev_pm = SCDetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE, -1);
773✔
331
        }
773✔
332
    } else if (idad->flags & ISDATAAT_RELATIVE) {
1,715✔
333
        prev_pm = SCDetectGetLastSMFromLists(s, DETECT_CONTENT, DETECT_PCRE, DETECT_BYTETEST,
1,593✔
334
                DETECT_BYTEJUMP, DETECT_BYTE_EXTRACT, DETECT_ISDATAAT, DETECT_BYTEMATH, -1);
1,593✔
335
        if (prev_pm == NULL)
1,593✔
336
            sm_list = DETECT_SM_LIST_PMATCH;
50✔
337
        else {
1,543✔
338
            sm_list = SigMatchListSMBelongsTo(s, prev_pm);
1,543✔
339
            if (sm_list < 0)
1,543✔
340
                goto end;
×
341
        }
1,543✔
342
    } else {
1,593✔
343
        sm_list = DETECT_SM_LIST_PMATCH;
122✔
344
    }
122✔
345

346
    if (offset != NULL) {
3,054✔
347
        DetectByteIndexType index;
212✔
348
        if (!DetectByteRetrieveSMVar(offset, s, -1, &index)) {
212✔
349
            SCLogError("Unknown byte_extract var "
146✔
350
                       "seen in isdataat - %s\n",
146✔
351
                    offset);
146✔
352
            goto end;
146✔
353
        }
146✔
354
        idad->dataat = index;
66✔
355
        idad->flags |= ISDATAAT_OFFSET_VAR;
66✔
356
        SCLogDebug("isdataat uses byte_extract with local id %u", idad->dataat);
66✔
357
        SCFree(offset);
66✔
358
        offset = NULL;
66✔
359
    }
66✔
360

361
    /* 'ends with' scenario */
362
    if (prev_pm != NULL && prev_pm->type == DETECT_CONTENT &&
2,908✔
363
        idad->dataat == 1 &&
2,908✔
364
        (idad->flags & (ISDATAAT_RELATIVE|ISDATAAT_NEGATED)) == (ISDATAAT_RELATIVE|ISDATAAT_NEGATED))
2,908✔
365
    {
161✔
366
        DetectIsdataatFree(de_ctx, idad);
161✔
367
        DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
161✔
368
        cd->flags |= DETECT_CONTENT_ENDS_WITH;
161✔
369
        ret = 0;
161✔
370
        goto end;
161✔
371
    }
161✔
372

373
    if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_ISDATAAT, (SigMatchCtx *)idad, sm_list) ==
2,747✔
374
            NULL) {
2,747✔
375
        goto end;
×
376
    }
×
377

378
    if (!(idad->flags & ISDATAAT_RELATIVE)) {
2,747✔
379
        ret = 0;
542✔
380
        goto end;
542✔
381
    }
542✔
382

383
    if (prev_pm == NULL) {
2,205✔
384
        ret = 0;
307✔
385
        goto end;
307✔
386
    }
307✔
387

388
    if (prev_pm->type == DETECT_CONTENT) {
1,898✔
389
        DetectContentData *cd = (DetectContentData *)prev_pm->ctx;
944✔
390
        cd->flags |= DETECT_CONTENT_RELATIVE_NEXT;
944✔
391
    } else if (prev_pm->type == DETECT_PCRE) {
954✔
392
        DetectPcreData *pd = (DetectPcreData *)prev_pm->ctx;
338✔
393
        pd->flags |= DETECT_PCRE_RELATIVE_NEXT;
338✔
394
    }
338✔
395

396
    ret = 0;
1,898✔
397

398
end:
3,055✔
399
    if (offset)
3,055✔
400
        SCFree(offset);
146✔
401
    if (ret != 0)
3,055✔
402
        DetectIsdataatFree(de_ctx, idad);
147✔
403
    return ret;
3,055✔
404
}
1,898✔
405

406
/**
407
 * \brief this function will free memory associated with DetectIsdataatData
408
 *
409
 * \param idad pointer to DetectIsdataatData
410
 */
411
void DetectIsdataatFree(DetectEngineCtx *de_ctx, void *ptr)
412
{
3,055✔
413
    DetectIsdataatData *idad = (DetectIsdataatData *)ptr;
3,055✔
414
    SCFree(idad);
3,055✔
415
}
3,055✔
416

417
static int DetectEndsWithSetup (DetectEngineCtx *de_ctx, Signature *s, const char *nullstr)
418
{
38,974✔
419
    SigMatch *pm = NULL;
38,974✔
420
    int ret = -1;
38,974✔
421

422
    /* retrieve the sm to apply the depth against */
423
    pm = SCDetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
38,974✔
424
    if (pm == NULL) {
38,974✔
425
        SCLogError("endswith needs a "
812✔
426
                   "preceding content option");
812✔
427
        goto end;
812✔
428
    }
812✔
429

430
    /* verify other conditions. */
431
    DetectContentData *cd = (DetectContentData *)pm->ctx;
38,162✔
432

433
    cd->flags |= DETECT_CONTENT_ENDS_WITH;
38,162✔
434

435
    ret = 0;
38,162✔
436
 end:
38,974✔
437
    return ret;
38,974✔
438
}
38,162✔
439

440
#ifdef UNITTESTS
441
static int g_dce_stub_data_buffer_id = 0;
442

443
/**
444
 * \test DetectIsdataatTestParse01 is a test to make sure that we return a correct IsdataatData structure
445
 *  when given valid isdataat opt
446
 */
447
static int DetectIsdataatTestParse01 (void)
448
{
449
    int result = 0;
450
    DetectIsdataatData *idad = NULL;
451
    idad = DetectIsdataatParse(NULL, "30 ", NULL);
452
    if (idad != NULL) {
453
        DetectIsdataatFree(NULL, idad);
454
        result = 1;
455
    }
456

457
    return result;
458
}
459

460
/**
461
 * \test DetectIsdataatTestParse02 is a test to make sure that we return a correct IsdataatData structure
462
 *  when given valid isdataat opt
463
 */
464
static int DetectIsdataatTestParse02 (void)
465
{
466
    int result = 0;
467
    DetectIsdataatData *idad = NULL;
468
    idad = DetectIsdataatParse(NULL, "30 , relative", NULL);
469
    if (idad != NULL && idad->flags & ISDATAAT_RELATIVE && !(idad->flags & ISDATAAT_RAWBYTES)) {
470
        DetectIsdataatFree(NULL, idad);
471
        result = 1;
472
    }
473

474
    return result;
475
}
476

477
/**
478
 * \test DetectIsdataatTestParse03 is a test to make sure that we return a correct IsdataatData structure
479
 *  when given valid isdataat opt
480
 */
481
static int DetectIsdataatTestParse03 (void)
482
{
483
    int result = 0;
484
    DetectIsdataatData *idad = NULL;
485
    idad = DetectIsdataatParse(NULL, "30,relative, rawbytes ", NULL);
486
    if (idad != NULL && idad->flags & ISDATAAT_RELATIVE && idad->flags & ISDATAAT_RAWBYTES) {
487
        DetectIsdataatFree(NULL, idad);
488
        result = 1;
489
    }
490

491
    return result;
492
}
493

494
/**
495
 * \test Test isdataat option for dce sig.
496
 */
497
static int DetectIsdataatTestParse04(void)
498
{
499
    Signature *s = SigAlloc();
500
    FAIL_IF_NULL(s);
501

502
    FAIL_IF(SCDetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0);
503

504
    FAIL_IF_NOT(DetectIsdataatSetup(NULL, s, "30") == 0);
505
    SigMatch *sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id);
506
    FAIL_IF_NOT_NULL(sm);
507
    FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_PMATCH]);
508
    SigFree(NULL, s);
509

510
    s = SigAlloc();
511
    FAIL_IF_NULL(s);
512
    FAIL_IF(SCDetectSignatureSetAppProto(s, ALPROTO_DCERPC) < 0);
513
    /* relative w/o preceeding match defaults to "pmatch" */
514
    FAIL_IF_NOT(DetectIsdataatSetup(NULL, s, "30,relative") == 0);
515
    sm = DetectBufferGetFirstSigMatch(s, g_dce_stub_data_buffer_id);
516
    FAIL_IF_NOT_NULL(sm);
517
    FAIL_IF_NULL(s->init_data->smlists[DETECT_SM_LIST_PMATCH]);
518

519
    SigFree(NULL, s);
520
    PASS;
521
}
522

523
static int DetectIsdataatTestParse06(void)
524
{
525
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
526
    FAIL_IF(de_ctx == NULL);
527
    de_ctx->flags |= DE_QUIET;
528

529
    Signature *s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
530
                               "(msg:\"Testing bytejump_body\"; "
531
                               "content:\"one\"; "
532
                               "isdataat:!4,relative; sid:1;)");
533
    FAIL_IF(s == NULL);
534

535
    FAIL_IF(s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH] == NULL);
536

537
    FAIL_IF_NOT(s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_ISDATAAT);
538
    DetectIsdataatData *data =
539
            (DetectIsdataatData *)s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->ctx;
540

541
    FAIL_IF_NOT(data->flags & ISDATAAT_RELATIVE);
542
    FAIL_IF(data->flags & ISDATAAT_RAWBYTES);
543
    FAIL_IF_NOT(data->flags & ISDATAAT_NEGATED);
544

545
    s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
546
                               "(msg:\"Testing bytejump_body\"; "
547
                               "content:\"one\"; "
548
                               "isdataat: !4,relative; sid:2;)");
549
    FAIL_IF(s == NULL);
550

551
    FAIL_IF(s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH] == NULL);
552

553
    FAIL_IF_NOT(s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_ISDATAAT);
554
    data = (DetectIsdataatData *)s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->ctx;
555

556
    FAIL_IF_NOT(data->flags & ISDATAAT_RELATIVE);
557
    FAIL_IF(data->flags & ISDATAAT_RAWBYTES);
558
    FAIL_IF_NOT(data->flags & ISDATAAT_NEGATED);
559
    DetectEngineCtxFree(de_ctx);
560

561
    PASS;
562
}
563

564
/**
565
 * \test DetectIsdataatTestPacket01 is a test to check matches of
566
 * isdataat, and isdataat relative
567
 */
568
static int DetectIsdataatTestPacket01 (void)
569
{
570
    int result = 0;
571
    uint8_t *buf = (uint8_t *)"Hi all!";
572
    uint16_t buflen = strlen((char *)buf);
573
    Packet *p[3];
574
    p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
575
    p[1] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_UDP);
576
    p[2] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_ICMP);
577

578
    if (p[0] == NULL || p[1] == NULL ||p[2] == NULL)
579
        goto end;
580

581
    const char *sigs[5];
582
    sigs[0]= "alert ip any any -> any any (msg:\"Testing window 1\"; isdataat:6; sid:1;)";
583
    sigs[1]= "alert ip any any -> any any (msg:\"Testing window 2\"; content:\"all\"; isdataat:1, relative; isdataat:6; sid:2;)";
584
    sigs[2]= "alert ip any any -> any any (msg:\"Testing window 3\"; isdataat:8; sid:3;)";
585
    sigs[3]= "alert ip any any -> any any (msg:\"Testing window 4\"; content:\"Hi\"; isdataat:5, relative; sid:4;)";
586
    sigs[4]= "alert ip any any -> any any (msg:\"Testing window 4\"; content:\"Hi\"; isdataat:6, relative; sid:5;)";
587

588
    uint32_t sid[5] = {1, 2, 3, 4, 5};
589

590
    uint32_t results[3][5] = {
591
                              /* packet 0 match sid 1 but should not match sid 2 */
592
                              {1, 1, 0, 1, 0},
593
                              /* packet 1 should not match */
594
                              {1, 1, 0, 1, 0},
595
                              /* packet 2 should not match */
596
                              {1, 1, 0, 1, 0} };
597

598
    result = UTHGenericTest(p, 3, sigs, sid, (uint32_t *) results, 5);
599

600
    UTHFreePackets(p, 3);
601
end:
602
    return result;
603
}
604

605
/**
606
 * \test DetectIsdataatTestPacket02 is a test to check matches of
607
 * isdataat, and isdataat relative works if the previous keyword is pcre
608
 * (bug 144)
609
 */
610
static int DetectIsdataatTestPacket02 (void)
611
{
612
    int result = 0;
613
    uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
614
                    "User-Agent: Wget/1.11.4"
615
                    "Accept: */*"
616
                    "Host: www.google.com"
617
                    "Connection: Keep-Alive"
618
                    "Date: Mon, 04 Jan 2010 17:29:39 GMT";
619
    uint16_t buflen = strlen((char *)buf);
620
    Packet *p;
621
    p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
622

623
    if (p == NULL)
624
        goto end;
625

626
    char sig[] = "alert tcp any any -> any any (msg:\"pcre with"
627
            " isdataat + relative\"; pcre:\"/A(ll|pp)WorkAndNoPlayMakesWillA"
628
            "DullBoy/\"; isdataat:96,relative; sid:1;)";
629

630
    result = UTHPacketMatchSig(p, sig);
631

632
    UTHFreePacket(p);
633
end:
634
    return result;
635
}
636

637
/**
638
 * \test DetectIsdataatTestPacket03 is a test to check matches of
639
 * isdataat, and isdataat relative works if the previous keyword is byte_jump
640
 * (bug 146)
641
 */
642
static int DetectIsdataatTestPacket03 (void)
643
{
644
    int result = 0;
645
    uint8_t *buf = (uint8_t *)"GET /AllWorkAndNoPlayMakesWillADullBoy HTTP/1.0"
646
                    "User-Agent: Wget/1.11.4"
647
                    "Accept: */*"
648
                    "Host: www.google.com"
649
                    "Connection: Keep-Alive"
650
                    "Date: Mon, 04 Jan 2010 17:29:39 GMT";
651
    uint16_t buflen = strlen((char *)buf);
652
    Packet *p;
653
    p = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP);
654

655
    if (p == NULL)
656
        goto end;
657

658
    char sig[] = "alert tcp any any -> any any (msg:\"byte_jump match = 0 "
659
    "with distance content HTTP/1. relative against HTTP/1.0\"; byte_jump:1,"
660
    "46,string,dec; isdataat:87,relative; sid:109; rev:1;)";
661

662
    result = UTHPacketMatchSig(p, sig);
663

664
    UTHFreePacket(p);
665
end:
666
    return result;
667
}
668

669
/**
670
 * \brief this function registers unit tests for DetectIsdataat
671
 */
672
void DetectIsdataatRegisterTests(void)
673
{
674
    g_dce_stub_data_buffer_id = DetectBufferTypeGetByName("dce_stub_data");
675

676
    UtRegisterTest("DetectIsdataatTestParse01", DetectIsdataatTestParse01);
677
    UtRegisterTest("DetectIsdataatTestParse02", DetectIsdataatTestParse02);
678
    UtRegisterTest("DetectIsdataatTestParse03", DetectIsdataatTestParse03);
679
    UtRegisterTest("DetectIsdataatTestParse04", DetectIsdataatTestParse04);
680
    UtRegisterTest("DetectIsdataatTestParse06", DetectIsdataatTestParse06);
681

682
    UtRegisterTest("DetectIsdataatTestPacket01", DetectIsdataatTestPacket01);
683
    UtRegisterTest("DetectIsdataatTestPacket02", DetectIsdataatTestPacket02);
684
    UtRegisterTest("DetectIsdataatTestPacket03", DetectIsdataatTestPacket03);
685
}
686

687
static int DetectAbsentTestParse01(void)
688
{
689
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
690
    FAIL_IF(de_ctx == NULL);
691
    de_ctx->flags |= DE_QUIET;
692

693
    Signature *s = DetectEngineAppendSig(de_ctx,
694
            "alert http any any -> any any "
695
            "(msg:\"invalid absent only with negated content\"; http.user_agent; "
696
            "absent; content:!\"one\"; sid:2;)");
697
    FAIL_IF(s != NULL);
698
    s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any "
699
                                      "(msg:\"invalid absent\"; http.user_agent; "
700
                                      "content:!\"one\"; absent; sid:2;)");
701
    FAIL_IF(s != NULL);
702
    s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any "
703
                                      "(msg:\"invalid absent\"; http.user_agent; "
704
                                      "content:\"one\"; absent: or_else; sid:2;)");
705
    FAIL_IF(s != NULL);
706
    s = DetectEngineAppendSig(de_ctx, "alert http any any -> any any "
707
                                      "(msg:\"absent without sticky buffer\"; "
708
                                      "content:!\"one\"; absent: or_else; sid:2;)");
709
    FAIL_IF(s != NULL);
710
    s = DetectEngineAppendSig(de_ctx,
711
            "alert websocket any any -> any any "
712
            "(msg:\"absent with frame\"; "
713
            "frame: websocket.pdu; absent: or_else; content:!\"one\"; sid:2;)");
714
    FAIL_IF(s != NULL);
715
    DetectEngineCtxFree(de_ctx);
716
    PASS;
717
}
718

719
void DetectAbsentRegisterTests(void)
720
{
721
    UtRegisterTest("DetectAbsentTestParse01", DetectAbsentTestParse01);
722
}
723
#endif
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