Coverage Report

Created: 2026-01-16 07:00

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