Coverage Report

Created: 2026-11-12 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata8/src/detect-replace.c
Line
Count
Source
1
/* Copyright (C) 2011-2022 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 Eric Leblond <eric@regit.org>
22
 *
23
 * Replace part of the detection engine.
24
 *
25
 * If previous filter is of content type, replace can be used to change
26
 * the matched part to a new value.
27
 */
28
29
#include "suricata-common.h"
30
31
#include "runmodes.h"
32
33
#include "decode.h"
34
35
#include "detect.h"
36
#include "detect-parse.h"
37
#include "detect-content.h"
38
#include "detect-replace.h"
39
#include "app-layer.h"
40
41
#include "detect-engine-mpm.h"
42
#include "detect-engine.h"
43
#include "detect-engine-build.h"
44
45
#include "util-checksum.h"
46
47
#include "util-unittest.h"
48
49
#include "util-debug.h"
50
51
static int DetectReplaceSetup(DetectEngineCtx *, Signature *, const char *);
52
#ifdef UNITTESTS
53
static void DetectReplaceRegisterTests(void);
54
#endif
55
static int DetectReplacePostMatch(DetectEngineThreadCtx *det_ctx,
56
        Packet *p, const Signature *s, const SigMatchCtx *ctx);
57
58
void DetectReplaceRegister (void)
59
12
{
60
12
    sigmatch_table[DETECT_REPLACE].name = "replace";
61
12
    sigmatch_table[DETECT_REPLACE].desc = "only to be used in IPS-mode. Change the following content into another";
62
12
    sigmatch_table[DETECT_REPLACE].url = "/rules/payload-keywords.html#replace";
63
12
    sigmatch_table[DETECT_REPLACE].Match = DetectReplacePostMatch;
64
12
    sigmatch_table[DETECT_REPLACE].Setup = DetectReplaceSetup;
65
#ifdef UNITTESTS
66
    sigmatch_table[DETECT_REPLACE].RegisterTests = DetectReplaceRegisterTests;
67
#endif
68
12
    sigmatch_table[DETECT_REPLACE].flags = (SIGMATCH_QUOTES_MANDATORY|SIGMATCH_HANDLE_NEGATION);
69
12
}
70
71
static int DetectReplacePostMatch(DetectEngineThreadCtx *det_ctx,
72
        Packet *p, const Signature *s, const SigMatchCtx *ctx)
73
0
{
74
0
    if (det_ctx->replist) {
75
0
        DetectReplaceExecuteInternal(p, det_ctx->replist);
76
0
        det_ctx->replist = NULL;
77
0
    }
78
0
    return 1;
79
0
}
80
81
int DetectReplaceSetup(DetectEngineCtx *de_ctx, Signature *s, const char *replacestr)
82
1.06k
{
83
1.06k
    uint8_t *content = NULL;
84
1.06k
    uint16_t len = 0;
85
86
1.06k
    if (s->init_data->negated) {
87
89
        SCLogError("Can't negate replacement string: %s", replacestr);
88
89
        return -1;
89
89
    }
90
91
979
    switch (SCRunmodeGet()) {
92
0
        case RUNMODE_NFQ:
93
0
        case RUNMODE_IPFW:
94
0
            break;
95
979
        default:
96
979
            SCLogWarning("Can't use 'replace' keyword in non IPS mode: %s", s->sig_str);
97
            /* this is a success, having the alert is interesting */
98
979
            return 0;
99
979
    }
100
101
0
    int ret = DetectContentDataParse("replace", replacestr, &content, &len);
102
0
    if (ret == -1)
103
0
        return -1;
104
105
    /* add to the latest "content" keyword from pmatch */
106
0
    const SigMatch *pm = DetectGetLastSMByListId(s, DETECT_SM_LIST_PMATCH,
107
0
            DETECT_CONTENT, -1);
108
0
    if (pm == NULL) {
109
0
        SCLogError("replace needs"
110
0
                   "preceding content option for raw sig");
111
0
        SCFree(content);
112
0
        return -1;
113
0
    }
114
115
    /* we can remove this switch now with the unified structure */
116
0
    DetectContentData *ud = (DetectContentData *)pm->ctx;
117
0
    if (ud == NULL) {
118
0
        SCLogError("invalid argument");
119
0
        SCFree(content);
120
0
        return -1;
121
0
    }
122
0
    if (ud->flags & DETECT_CONTENT_NEGATED) {
123
0
        SCLogError("can't have a relative "
124
0
                   "negated keyword set along with a replacement");
125
0
        goto error;
126
0
    }
127
0
    if (ud->content_len != len) {
128
0
        SCLogError("can't have a content "
129
0
                   "length different from replace length");
130
0
        goto error;
131
0
    }
132
133
0
    ud->replace = SCMalloc(len);
134
0
    if (ud->replace == NULL) {
135
0
        goto error;
136
0
    }
137
0
    memcpy(ud->replace, content, len);
138
0
    ud->replace_len = len;
139
0
    ud->flags |= DETECT_CONTENT_REPLACE;
140
    /* want packet matching only won't be able to replace data with
141
     * a flow.
142
     */
143
0
    s->flags |= SIG_FLAG_REQUIRE_PACKET;
144
0
    SCFree(content);
145
0
    content = NULL;
146
147
0
    if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_REPLACE, NULL, DETECT_SM_LIST_POSTMATCH) ==
148
0
            NULL) {
149
0
        goto error;
150
0
    }
151
0
    return 0;
152
153
0
error:
154
0
    SCFree(ud->replace);
155
0
    ud->replace = NULL;
156
0
    SCFree(content);
157
0
    return -1;
158
0
}
159
160
/* Add to the head of the replace-list.
161
 *
162
 * The first to add to the replace-list has the highest priority. So,
163
 * adding the head of the list results in the newest modifications
164
 * of content being applied first, so later changes can over ride
165
 * earlier changes. Thus the highest priority modifications should be
166
 * applied last.
167
 */
168
DetectReplaceList *DetectReplaceAddToList(
169
        DetectReplaceList *replist, uint8_t *found, const DetectContentData *cd)
170
0
{
171
0
    if (cd->content_len != cd->replace_len)
172
0
        return NULL;
173
0
    SCLogDebug("replace: Adding match");
174
175
0
    DetectReplaceList *newlist = SCMalloc(sizeof(DetectReplaceList));
176
0
    if (unlikely(newlist == NULL))
177
0
        return replist;
178
0
    newlist->found = found;
179
0
    newlist->cd = cd;
180
    /* Push new value onto the front of the list. */
181
0
    newlist->next = replist;
182
0
    return newlist;
183
0
}
184
185
void DetectReplaceExecuteInternal(Packet *p, DetectReplaceList *replist)
186
0
{
187
0
    DetectReplaceList *tlist = NULL;
188
189
0
    SCLogDebug("replace: Executing match");
190
0
    while (replist) {
191
0
        memcpy(replist->found, replist->cd->replace, replist->cd->replace_len);
192
0
        SCLogDebug("replace: replaced data");
193
0
        p->flags |= PKT_STREAM_MODIFIED;
194
0
        ReCalculateChecksum(p);
195
0
        tlist = replist;
196
0
        replist = replist->next;
197
0
        SCFree(tlist);
198
0
    }
199
0
}
200
201
202
void DetectReplaceFreeInternal(DetectReplaceList *replist)
203
0
{
204
0
    DetectReplaceList *tlist = NULL;
205
0
    while (replist) {
206
0
        SCLogDebug("replace: Freeing match");
207
0
        tlist = replist;
208
0
        replist = replist->next;
209
0
        SCFree(tlist);
210
0
    }
211
0
}
212
213
#ifdef UNITTESTS /* UNITTESTS */
214
#include "detect-engine-alert.h"
215
#include "packet.h"
216
217
/**
218
 * \test Test packet Matches
219
 * \param raw_eth_pkt pointer to the ethernet packet
220
 * \param pktsize size of the packet
221
 * \param sig pointer to the signature to test
222
 * \param sid sid number of the signature
223
 * \retval return 1 if match
224
 * \retval return 0 if not
225
 */
226
static
227
int DetectReplaceLongPatternMatchTest(uint8_t *raw_eth_pkt, uint16_t pktsize,
228
                                      const char *sig, uint32_t sid, uint8_t *pp,
229
                                      uint16_t *len)
230
{
231
    int result = 0;
232
233
    Packet *p = NULL;
234
    p = PacketGetFromAlloc();
235
    if (unlikely(p == NULL))
236
        return 0;
237
238
    DecodeThreadVars dtv;
239
240
    ThreadVars th_v;
241
    DetectEngineThreadCtx *det_ctx = NULL;
242
243
    if (pp == NULL) {
244
        SCLogDebug("replace: looks like a second run");
245
    }
246
247
    PacketCopyData(p, raw_eth_pkt, pktsize);
248
    memset(&dtv, 0, sizeof(DecodeThreadVars));
249
    memset(&th_v, 0, sizeof(th_v));
250
    dtv.app_tctx = AppLayerGetCtxThread();
251
252
    FlowInitConfig(FLOW_QUIET);
253
    DecodeEthernet(&th_v, &dtv, p, GET_PKT_DATA(p), pktsize);
254
255
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
256
    if (de_ctx == NULL) {
257
        goto end;
258
    }
259
    de_ctx->flags |= DE_QUIET;
260
261
    de_ctx->sig_list = SigInit(de_ctx, sig);
262
    if (de_ctx->sig_list == NULL) {
263
        goto end;
264
    }
265
    de_ctx->sig_list->next = NULL;
266
267
    if (de_ctx->sig_list->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_CONTENT) {
268
        DetectContentData *co = (DetectContentData *)de_ctx->sig_list->init_data
269
                                        ->smlists_tail[DETECT_SM_LIST_PMATCH]
270
                                        ->ctx;
271
        if (co->flags & DETECT_CONTENT_RELATIVE_NEXT) {
272
            printf("relative next flag set on final match which is content: ");
273
            goto end;
274
        }
275
    }
276
277
    SigGroupBuild(de_ctx);
278
    DetectEngineAddToMaster(de_ctx);
279
    DetectEngineThreadCtxInit(&th_v, NULL, (void *)&det_ctx);
280
281
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
282
    DetectEngineMoveToFreeList(de_ctx);
283
284
    if (PacketAlertCheck(p, sid) != 1) {
285
        SCLogDebug("replace: no alert on sig %d", sid);
286
        goto end;
287
    }
288
289
    if (pp) {
290
        memcpy(pp, GET_PKT_DATA(p), GET_PKT_LEN(p));
291
        *len = pktsize;
292
        SCLogDebug("replace: copying %d on %p", *len, pp);
293
    }
294
295
296
    result = 1;
297
end:
298
    if (dtv.app_tctx != NULL)
299
        AppLayerDestroyCtxThread(dtv.app_tctx);
300
    if (det_ctx != NULL)
301
        DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
302
    DetectEnginePruneFreeList();
303
    PacketRecycle(p);
304
    FlowShutdown();
305
    SCFree(p);
306
307
308
    return result;
309
}
310
311
312
/**
313
 * \brief Wrapper for DetectContentLongPatternMatchTest
314
 */
315
static int DetectReplaceLongPatternMatchTestWrp(const char *sig, uint32_t sid, const char *sig_rep,  uint32_t sid_rep)
316
{
317
    int ret;
318
    /** Real packet with the following tcp data:
319
     * "Hi, this is a big test to check content matches of splitted"
320
     * "patterns between multiple chunks!"
321
     * (without quotes! :) )
322
     */
323
    uint8_t raw_eth_pkt[] = {
324
        0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
325
        0x00,0x00,0x00,0x00,0x08,0x00,0x45,0x00,
326
        0x00,0x85,0x00,0x01,0x00,0x00,0x40,0x06,
327
        0x7c,0x70,0x7f,0x00,0x00,0x01,0x7f,0x00,
328
        0x00,0x01,0x00,0x14,0x00,0x50,0x00,0x00,
329
        0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,
330
        0x20,0x00,0xc9,0xad,0x00,0x00,0x48,0x69,
331
        0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
332
        0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
333
        0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
334
        0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
335
        0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
336
        0x74,0x63,0x68,0x65,0x73,0x20,0x6f,0x66,
337
        0x20,0x73,0x70,0x6c,0x69,0x74,0x74,0x65,
338
        0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
339
        0x6e,0x73,0x20,0x62,0x65,0x74,0x77,0x65,
340
        0x65,0x6e,0x20,0x6d,0x75,0x6c,0x74,0x69,
341
        0x70,0x6c,0x65,0x20,0x63,0x68,0x75,0x6e,
342
        0x6b,0x73,0x21 }; /* end raw_eth_pkt */
343
    uint8_t p[sizeof(raw_eth_pkt)];
344
    uint16_t psize = sizeof(raw_eth_pkt);
345
346
    /* would be unittest */
347
    int run_mode_backup = SCRunmodeGet();
348
    SCRunmodeSet(RUNMODE_NFQ);
349
    ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
350
                             sig, sid, p, &psize);
351
    if (ret == 1) {
352
        SCLogDebug("replace: test1 phase1");
353
        ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL);
354
    }
355
    SCRunmodeSet(run_mode_backup);
356
    return ret;
357
}
358
359
360
/**
361
 * \brief Wrapper for DetectContentLongPatternMatchTest
362
 */
363
static int DetectReplaceLongPatternMatchTestUDPWrp(const char *sig, uint32_t sid, const char *sig_rep,  uint32_t sid_rep)
364
{
365
    int ret;
366
    /** Real UDP DNS packet with a request A to a1.twimg.com
367
     */
368
    uint8_t raw_eth_pkt[] = {
369
        0x8c, 0xa9, 0x82, 0x75, 0x5d, 0x62, 0xb4, 0x07, 
370
        0xf9, 0xf3, 0xc7, 0x0a, 0x08, 0x00, 0x45, 0x00, 
371
        0x00, 0x3a, 0x92, 0x4f, 0x40, 0x00, 0x40, 0x11, 
372
        0x31, 0x1a, 0xc0, 0xa8, 0x00, 0x02, 0xc1, 0xbd, 
373
        0xf4, 0xe1, 0x3b, 0x7e, 0x00, 0x35, 0x00, 0x26, 
374
        0xcb, 0x81, 0x37, 0x62, 0x01, 0x00, 0x00, 0x01, 
375
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, 
376
        0x31, 0x05, 0x74, 0x77, 0x69, 0x6d, 0x67, 0x03, 
377
        0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 };
378
379
    uint8_t p[sizeof(raw_eth_pkt)];
380
    uint16_t psize = sizeof(raw_eth_pkt);
381
382
    int run_mode_backup = SCRunmodeGet();
383
    SCRunmodeSet(RUNMODE_NFQ);
384
    ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
385
                             sig, sid, p, &psize);
386
    if (ret == 1) {
387
        SCLogDebug("replace: test1 phase1 ok: %" PRIuMAX" vs %d",(uintmax_t)sizeof(raw_eth_pkt),psize);
388
        ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL);
389
    }
390
    SCRunmodeSet(run_mode_backup);
391
    return ret;
392
}
393
394
/**
395
 * \test Check if replace is working
396
 */
397
static int DetectReplaceMatchTest01(void)
398
{
399
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
400
                " content:\"big\"; replace:\"pig\"; sid:1;)";
401
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
402
                " content:\"this is a pig test\"; sid:2;)";
403
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
404
    PASS;
405
}
406
407
/**
408
 * \test Check if replace is working with offset
409
 */
410
static int DetectReplaceMatchTest02(void)
411
{
412
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
413
                " content:\"th\"; offset: 4; replace:\"TH\"; sid:1;)";
414
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
415
                " content:\"THis\"; offset:4; sid:2;)";
416
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
417
    PASS;
418
}
419
420
/**
421
 * \test Check if replace is working with offset and keyword inversion
422
 */
423
static int DetectReplaceMatchTest03(void)
424
{
425
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
426
                " content:\"th\"; replace:\"TH\"; offset: 4; sid:1;)";
427
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
428
                " content:\"THis\"; offset:4; sid:2;)";
429
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
430
    PASS;
431
}
432
433
/**
434
 * \test Check if replace is working with second content
435
 */
436
static int DetectReplaceMatchTest04(void)
437
{
438
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
439
                " content:\"th\"; replace:\"TH\"; content:\"patter\"; replace:\"matter\"; sid:1;)";
440
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
441
                " content:\"THis\"; content:\"matterns\"; sid:2;)";
442
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
443
    PASS;
444
}
445
446
/**
447
 * \test Check if replace is not done when second content don't match
448
 */
449
static int DetectReplaceMatchTest05(void)
450
{
451
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
452
                " content:\"th\"; replace:\"TH\"; content:\"nutella\"; sid:1;)";
453
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
454
                " content:\"TH\"; sid:2;)";
455
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
456
    PASS;
457
}
458
459
/**
460
 * \test Check if replace is not done when second content match and not
461
 * first
462
 */
463
static int DetectReplaceMatchTest06(void)
464
{
465
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
466
                " content:\"nutella\"; replace:\"commode\"; content:\"this is\"; sid:1;)";
467
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
468
                " content:\"commode\"; sid:2;)";
469
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
470
    PASS;
471
}
472
473
/**
474
 * \test Check if replace is working when nocase used
475
 */
476
static int DetectReplaceMatchTest07(void)
477
{
478
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
479
                " content:\"BiG\"; nocase; replace:\"pig\"; sid:1;)";
480
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
481
                " content:\"this is a pig test\"; sid:2;)";
482
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
483
    PASS;
484
}
485
486
/**
487
 * \test Check if replace is working when depth is used
488
 */
489
static int DetectReplaceMatchTest08(void)
490
{
491
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
492
                " content:\"big\"; depth:17; replace:\"pig\"; sid:1;)";
493
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
494
                " content:\"this is a pig test\"; sid:2;)";
495
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
496
    PASS;
497
}
498
499
/**
500
 * \test Check if replace is working when depth block match used
501
 */
502
static int DetectReplaceMatchTest09(void)
503
{
504
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
505
                " content:\"big\"; depth:16; replace:\"pig\"; sid:1;)";
506
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
507
                " content:\"this is a pig test\"; sid:2;)";
508
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
509
    PASS;
510
}
511
512
/**
513
 * \test Check if replace is working when depth block match used
514
 */
515
static int DetectReplaceMatchTest10(void)
516
{
517
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
518
                " content:\"big\"; depth:17; replace:\"pig\"; offset: 14; sid:1;)";
519
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
520
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
521
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
522
    PASS;
523
}
524
525
/**
526
 * \test Check if replace is working with within
527
 */
528
static int DetectReplaceMatchTest11(void)
529
{
530
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
531
                " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 11; sid:1;)";
532
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
533
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
534
535
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
536
    PASS;
537
}
538
539
/**
540
 * \test Check if replace is working with within
541
 */
542
static int DetectReplaceMatchTest12(void)
543
{
544
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
545
                " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 4; sid:1;)";
546
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
547
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
548
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
549
    PASS;
550
}
551
552
/**
553
 * \test Check if replace is working with within
554
 */
555
static int DetectReplaceMatchTest13(void)
556
{
557
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
558
                " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 1; sid:1;)";
559
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
560
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
561
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
562
    PASS;
563
}
564
565
/**
566
 * \test Check if replace is working with within
567
 */
568
static int DetectReplaceMatchTest14(void)
569
{
570
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
571
                " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 2; sid:1;)";
572
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
573
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
574
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
575
    PASS;
576
}
577
578
/**
579
 * \test Check if replace is working with within
580
 */
581
static int DetectReplaceMatchTest15(void)
582
{
583
    const char *sig = "alert udp any any -> any any (msg:\"Nothing..\";"
584
                " content:\"com\"; replace:\"org\"; sid:1;)";
585
    const char *sig_rep = "alert udp any any -> any any (msg:\"replace worked\";"
586
                " content:\"twimg|03|org\"; sid:2;)";
587
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestUDPWrp(sig, 1, sig_rep, 2));
588
    PASS;
589
}
590
591
592
/**
593
 * \test Parsing test
594
 */
595
static int DetectReplaceParseTest01(void)
596
{
597
    int run_mode_backup = SCRunmodeGet();
598
    SCRunmodeSet(RUNMODE_NFQ);
599
600
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
601
    FAIL_IF_NULL(de_ctx);
602
603
    de_ctx->flags |= DE_QUIET;
604
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
605
            "alert udp any any -> any any "
606
            "(msg:\"test\"; content:\"doh\"; replace:\"; sid:238012;)"));
607
608
    SCRunmodeSet(run_mode_backup);
609
    DetectEngineCtxFree(de_ctx);
610
    PASS;
611
}
612
613
/**
614
 * \test Parsing test: non valid because of http protocol
615
 */
616
static int DetectReplaceParseTest02(void)
617
{
618
    int run_mode_backup = SCRunmodeGet();
619
    SCRunmodeSet(RUNMODE_NFQ);
620
621
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
622
    FAIL_IF_NULL(de_ctx);
623
624
    de_ctx->flags |= DE_QUIET;
625
    FAIL_IF_NULL(DetectEngineAppendSig(de_ctx,
626
            "alert http any any -> any any "
627
            "(msg:\"test\"; content:\"doh\"; replace:\"bon\"; sid:238012;)"));
628
629
    SCRunmodeSet(run_mode_backup);
630
    DetectEngineCtxFree(de_ctx);
631
    PASS;
632
}
633
634
/**
635
 * \test Parsing test: non valid because of http_header on same content
636
 * as replace keyword
637
 */
638
static int DetectReplaceParseTest03(void)
639
{
640
    int run_mode_backup = SCRunmodeGet();
641
    SCRunmodeSet(RUNMODE_NFQ);
642
643
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
644
645
    FAIL_IF_NULL(de_ctx);
646
647
    de_ctx->flags |= DE_QUIET;
648
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
649
            "alert tcp any any -> any any "
650
            "(msg:\"test\"; content:\"doh\"; replace:\"don\"; http_header; sid:238012;)"));
651
652
    SCRunmodeSet(run_mode_backup);
653
    DetectEngineCtxFree(de_ctx);
654
    PASS;
655
}
656
657
/**
658
 * \test Parsing test no content
659
 */
660
static int DetectReplaceParseTest04(void)
661
{
662
    int run_mode_backup = SCRunmodeGet();
663
    SCRunmodeSet(RUNMODE_NFQ);
664
665
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
666
    FAIL_IF_NULL(de_ctx);
667
668
    de_ctx->flags |= DE_QUIET;
669
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
670
                                                   "(msg:\"test\"; replace:\"don\"; sid:238012;)"));
671
672
    SCRunmodeSet(run_mode_backup);
673
    DetectEngineCtxFree(de_ctx);
674
    PASS;
675
}
676
677
/**
678
 * \test Parsing test content after replace
679
 */
680
static int DetectReplaceParseTest05(void)
681
{
682
    int run_mode_backup = SCRunmodeGet();
683
    SCRunmodeSet(RUNMODE_NFQ);
684
685
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
686
    FAIL_IF_NULL(de_ctx);
687
688
    de_ctx->flags |= DE_QUIET;
689
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
690
            "alert tcp any any -> any any "
691
            "(msg:\"test\"; replace:\"don\"; content:\"doh\"; sid:238012;)"));
692
693
    SCRunmodeSet(run_mode_backup);
694
    DetectEngineCtxFree(de_ctx);
695
    PASS;
696
}
697
698
/**
699
 * \test Parsing test content and replace length differ
700
 */
701
static int DetectReplaceParseTest06(void)
702
{
703
    int run_mode_backup = SCRunmodeGet();
704
    SCRunmodeSet(RUNMODE_NFQ);
705
706
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
707
    FAIL_IF_NULL(de_ctx);
708
709
    de_ctx->flags |= DE_QUIET;
710
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
711
            "alert tcp any any -> any any "
712
            "(msg:\"test\"; content:\"don\"; replace:\"donut\"; sid:238012;)"));
713
714
    SCRunmodeSet(run_mode_backup);
715
    DetectEngineCtxFree(de_ctx);
716
    PASS;
717
}
718
719
/**
720
 * \test Parsing test content and replace length differ
721
 */
722
static int DetectReplaceParseTest07(void)
723
{
724
    int run_mode_backup = SCRunmodeGet();
725
    SCRunmodeSet(RUNMODE_NFQ);
726
727
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
728
    FAIL_IF_NULL(de_ctx);
729
730
    de_ctx->flags |= DE_QUIET;
731
    FAIL_IF_NOT_NULL(
732
            DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
733
                                          "(msg:\"test\"; content:\"don\"; replace:\"dou\"; "
734
                                          "content:\"jpg\"; http_header; sid:238012;)"));
735
736
    SCRunmodeSet(run_mode_backup);
737
    DetectEngineCtxFree(de_ctx);
738
    PASS;
739
}
740
741
/**
742
 * \brief this function registers unit tests for DetectContent
743
 */
744
void DetectReplaceRegisterTests(void)
745
{
746
/* matching */
747
    UtRegisterTest("DetectReplaceMatchTest01", DetectReplaceMatchTest01);
748
    UtRegisterTest("DetectReplaceMatchTest02", DetectReplaceMatchTest02);
749
    UtRegisterTest("DetectReplaceMatchTest03", DetectReplaceMatchTest03);
750
    UtRegisterTest("DetectReplaceMatchTest04", DetectReplaceMatchTest04);
751
    UtRegisterTest("DetectReplaceMatchTest05", DetectReplaceMatchTest05);
752
    UtRegisterTest("DetectReplaceMatchTest06", DetectReplaceMatchTest06);
753
    UtRegisterTest("DetectReplaceMatchTest07", DetectReplaceMatchTest07);
754
    UtRegisterTest("DetectReplaceMatchTest08", DetectReplaceMatchTest08);
755
    UtRegisterTest("DetectReplaceMatchTest09", DetectReplaceMatchTest09);
756
    UtRegisterTest("DetectReplaceMatchTest10", DetectReplaceMatchTest10);
757
    UtRegisterTest("DetectReplaceMatchTest11", DetectReplaceMatchTest11);
758
    UtRegisterTest("DetectReplaceMatchTest12", DetectReplaceMatchTest12);
759
    UtRegisterTest("DetectReplaceMatchTest13", DetectReplaceMatchTest13);
760
    UtRegisterTest("DetectReplaceMatchTest14", DetectReplaceMatchTest14);
761
    UtRegisterTest("DetectReplaceMatchTest15", DetectReplaceMatchTest15);
762
/* parsing */
763
    UtRegisterTest("DetectReplaceParseTest01", DetectReplaceParseTest01);
764
    UtRegisterTest("DetectReplaceParseTest02", DetectReplaceParseTest02);
765
    UtRegisterTest("DetectReplaceParseTest03", DetectReplaceParseTest03);
766
    UtRegisterTest("DetectReplaceParseTest04", DetectReplaceParseTest04);
767
    UtRegisterTest("DetectReplaceParseTest05", DetectReplaceParseTest05);
768
    UtRegisterTest("DetectReplaceParseTest06", DetectReplaceParseTest06);
769
    UtRegisterTest("DetectReplaceParseTest07", DetectReplaceParseTest07);
770
}
771
#endif /* UNITTESTS */