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

OISF / suricata / 22550237931

01 Mar 2026 06:56PM UTC coverage: 64.812% (-8.9%) from 73.687%
22550237931

Pull #14920

github

web-flow
Merge e05854a6d into 90823fa90
Pull Request #14920: draft: rust based configuration file parser and loader - v4

561 of 789 new or added lines in 4 files covered. (71.1%)

23225 existing lines in 498 files now uncovered.

131605 of 203055 relevant lines covered (64.81%)

2328818.84 hits per line

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

17.86
/src/detect-replace.c
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
{
30✔
60
    sigmatch_table[DETECT_REPLACE].name = "replace";
30✔
61
    sigmatch_table[DETECT_REPLACE].desc = "only to be used in IPS-mode. Change the following content into another";
30✔
62
    sigmatch_table[DETECT_REPLACE].url = "/rules/payload-keywords.html#replace";
30✔
63
    sigmatch_table[DETECT_REPLACE].Match = DetectReplacePostMatch;
30✔
64
    sigmatch_table[DETECT_REPLACE].Setup = DetectReplaceSetup;
30✔
65
#ifdef UNITTESTS
66
    sigmatch_table[DETECT_REPLACE].RegisterTests = DetectReplaceRegisterTests;
67
#endif
68
    sigmatch_table[DETECT_REPLACE].flags = (SIGMATCH_QUOTES_MANDATORY|SIGMATCH_HANDLE_NEGATION);
30✔
69
}
30✔
70

71
static int DetectReplacePostMatch(DetectEngineThreadCtx *det_ctx,
72
        Packet *p, const Signature *s, const SigMatchCtx *ctx)
UNCOV
73
{
×
UNCOV
74
    if (det_ctx->replist) {
×
UNCOV
75
        DetectReplaceExecuteInternal(p, det_ctx->replist);
×
UNCOV
76
        det_ctx->replist = NULL;
×
UNCOV
77
    }
×
UNCOV
78
    return 1;
×
UNCOV
79
}
×
80

81
int DetectReplaceSetup(DetectEngineCtx *de_ctx, Signature *s, const char *replacestr)
82
{
1,105✔
83
    uint8_t *content = NULL;
1,105✔
84
    uint16_t len = 0;
1,105✔
85

86
    if (s->init_data->negated) {
1,105✔
87
        SCLogError("Can't negate replacement string: %s", replacestr);
64✔
88
        return -1;
64✔
89
    }
64✔
90

91
    switch (SCRunmodeGet()) {
1,041✔
UNCOV
92
        case RUNMODE_NFQ:
×
UNCOV
93
        case RUNMODE_IPFW:
×
UNCOV
94
            break;
×
95
        default:
1,041✔
96
            SCLogWarning("Can't use 'replace' keyword in non IPS mode: %s", s->sig_str);
1,041✔
97
            /* this is a success, having the alert is interesting */
98
            return 0;
1,041✔
99
    }
1,041✔
100

UNCOV
101
    int ret = DetectContentDataParse("replace", replacestr, &content, &len);
×
UNCOV
102
    if (ret == -1)
×
UNCOV
103
        return -1;
×
104

105
    /* add to the latest "content" keyword from pmatch */
UNCOV
106
    const SigMatch *pm = DetectGetLastSMByListId(s, DETECT_SM_LIST_PMATCH,
×
UNCOV
107
            DETECT_CONTENT, -1);
×
UNCOV
108
    if (pm == NULL) {
×
UNCOV
109
        SCLogError("replace needs"
×
UNCOV
110
                   "preceding content option for raw sig");
×
UNCOV
111
        SCFree(content);
×
UNCOV
112
        return -1;
×
UNCOV
113
    }
×
114

115
    /* we can remove this switch now with the unified structure */
UNCOV
116
    DetectContentData *ud = (DetectContentData *)pm->ctx;
×
UNCOV
117
    if (ud == NULL) {
×
118
        SCLogError("invalid argument");
×
119
        SCFree(content);
×
120
        return -1;
×
121
    }
×
UNCOV
122
    if (ud->flags & DETECT_CONTENT_NEGATED) {
×
123
        SCLogError("can't have a relative "
×
124
                   "negated keyword set along with a replacement");
×
125
        goto error;
×
126
    }
×
UNCOV
127
    if (ud->content_len != len) {
×
UNCOV
128
        SCLogError("can't have a content "
×
UNCOV
129
                   "length different from replace length");
×
UNCOV
130
        goto error;
×
UNCOV
131
    }
×
132

UNCOV
133
    ud->replace = SCMalloc(len);
×
UNCOV
134
    if (ud->replace == NULL) {
×
135
        goto error;
×
136
    }
×
UNCOV
137
    memcpy(ud->replace, content, len);
×
UNCOV
138
    ud->replace_len = len;
×
UNCOV
139
    ud->flags |= DETECT_CONTENT_REPLACE;
×
140
    /* want packet matching only won't be able to replace data with
141
     * a flow.
142
     */
UNCOV
143
    s->flags |= SIG_FLAG_REQUIRE_PACKET;
×
UNCOV
144
    SCFree(content);
×
UNCOV
145
    content = NULL;
×
146

UNCOV
147
    if (SCSigMatchAppendSMToList(de_ctx, s, DETECT_REPLACE, NULL, DETECT_SM_LIST_POSTMATCH) ==
×
UNCOV
148
            NULL) {
×
149
        goto error;
×
150
    }
×
UNCOV
151
    return 0;
×
152

UNCOV
153
error:
×
UNCOV
154
    SCFree(ud->replace);
×
UNCOV
155
    ud->replace = NULL;
×
UNCOV
156
    SCFree(content);
×
UNCOV
157
    return -1;
×
UNCOV
158
}
×
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)
UNCOV
170
{
×
UNCOV
171
    if (cd->content_len != cd->replace_len)
×
172
        return NULL;
×
UNCOV
173
    SCLogDebug("replace: Adding match");
×
174

UNCOV
175
    DetectReplaceList *newlist = SCMalloc(sizeof(DetectReplaceList));
×
UNCOV
176
    if (unlikely(newlist == NULL))
×
177
        return replist;
×
UNCOV
178
    newlist->found = found;
×
UNCOV
179
    newlist->cd = cd;
×
180
    /* Push new value onto the front of the list. */
UNCOV
181
    newlist->next = replist;
×
UNCOV
182
    return newlist;
×
UNCOV
183
}
×
184

185
void DetectReplaceExecuteInternal(Packet *p, DetectReplaceList *replist)
UNCOV
186
{
×
UNCOV
187
    DetectReplaceList *tlist = NULL;
×
188

UNCOV
189
    SCLogDebug("replace: Executing match");
×
UNCOV
190
    while (replist) {
×
UNCOV
191
        memcpy(replist->found, replist->cd->replace, replist->cd->replace_len);
×
UNCOV
192
        SCLogDebug("replace: replaced data");
×
UNCOV
193
        p->flags |= PKT_STREAM_MODIFIED;
×
UNCOV
194
        ReCalculateChecksum(p);
×
UNCOV
195
        tlist = replist;
×
UNCOV
196
        replist = replist->next;
×
UNCOV
197
        SCFree(tlist);
×
UNCOV
198
    }
×
UNCOV
199
}
×
200

201

202
void DetectReplaceFreeInternal(DetectReplaceList *replist)
UNCOV
203
{
×
UNCOV
204
    DetectReplaceList *tlist = NULL;
×
UNCOV
205
    while (replist) {
×
UNCOV
206
        SCLogDebug("replace: Freeing match");
×
UNCOV
207
        tlist = replist;
×
UNCOV
208
        replist = replist->next;
×
UNCOV
209
        SCFree(tlist);
×
UNCOV
210
    }
×
UNCOV
211
}
×
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 = PacketGetFromAlloc();
234
    if (unlikely(p == NULL))
235
        return 0;
236

237
    DecodeThreadVars dtv;
238
    ThreadVars th_v;
239
    memset(&dtv, 0, sizeof(DecodeThreadVars));
240
    memset(&th_v, 0, sizeof(th_v));
241
    StatsThreadInit(&th_v.stats);
242
    DetectEngineThreadCtx *det_ctx = NULL;
243

244
    if (pp == NULL) {
245
        SCLogDebug("replace: looks like a second run");
246
    }
247

248
    PacketCopyData(p, raw_eth_pkt, pktsize);
249
    dtv.app_tctx = AppLayerGetCtxThread();
250
    FlowInitConfig(FLOW_QUIET);
251
    DecodeEthernet(&th_v, &dtv, p, GET_PKT_DATA(p), pktsize);
252

253
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
254
    if (de_ctx == NULL) {
255
        goto end;
256
    }
257
    de_ctx->flags |= DE_QUIET;
258

259
    Signature *s = DetectEngineAppendSig(de_ctx, sig);
260
    if (s == NULL) {
261
        goto end;
262
    }
263

264
    if (s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->type == DETECT_CONTENT) {
265
        DetectContentData *co =
266
                (DetectContentData *)s->init_data->smlists_tail[DETECT_SM_LIST_PMATCH]->ctx;
267
        if (co->flags & DETECT_CONTENT_RELATIVE_NEXT) {
268
            printf("relative next flag set on final match which is content: ");
269
            goto end;
270
        }
271
    }
272

273
    SigGroupBuild(de_ctx);
274
    DetectEngineAddToMaster(de_ctx);
275
    DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
276
    BUG_ON(det_ctx == NULL);
277

278
    SigMatchSignatures(&th_v, de_ctx, det_ctx, p);
279
    DetectEngineMoveToFreeList(de_ctx);
280

281
    if (PacketAlertCheck(p, sid) != 1) {
282
        SCLogDebug("replace: no alert on sig %d", sid);
283
        goto end;
284
    }
285

286
    if (pp) {
287
        memcpy(pp, GET_PKT_DATA(p), GET_PKT_LEN(p));
288
        *len = pktsize;
289
        SCLogDebug("replace: copying %d on %p", *len, pp);
290
    }
291

292
    result = 1;
293
end:
294
    if (dtv.app_tctx != NULL)
295
        AppLayerDestroyCtxThread(dtv.app_tctx);
296
    if (det_ctx != NULL)
297
        DetectEngineThreadCtxDeinit(&th_v, (void *)det_ctx);
298
    DetectEnginePruneFreeList();
299
    PacketFree(p);
300
    FlowShutdown();
301
    StatsThreadCleanup(&th_v.stats);
302
    return result;
303
}
304

305

306
/**
307
 * \brief Wrapper for DetectContentLongPatternMatchTest
308
 */
309
static int DetectReplaceLongPatternMatchTestWrp(const char *sig, uint32_t sid, const char *sig_rep,  uint32_t sid_rep)
310
{
311
    int ret;
312
    /** Real packet with the following tcp data:
313
     * "Hi, this is a big test to check content matches of splitted"
314
     * "patterns between multiple chunks!"
315
     * (without quotes! :) )
316
     */
317
    uint8_t raw_eth_pkt[] = {
318
        0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,
319
        0x00,0x00,0x00,0x00,0x08,0x00,0x45,0x00,
320
        0x00,0x85,0x00,0x01,0x00,0x00,0x40,0x06,
321
        0x7c,0x70,0x7f,0x00,0x00,0x01,0x7f,0x00,
322
        0x00,0x01,0x00,0x14,0x00,0x50,0x00,0x00,
323
        0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x02,
324
        0x20,0x00,0xc9,0xad,0x00,0x00,0x48,0x69,
325
        0x2c,0x20,0x74,0x68,0x69,0x73,0x20,0x69,
326
        0x73,0x20,0x61,0x20,0x62,0x69,0x67,0x20,
327
        0x74,0x65,0x73,0x74,0x20,0x74,0x6f,0x20,
328
        0x63,0x68,0x65,0x63,0x6b,0x20,0x63,0x6f,
329
        0x6e,0x74,0x65,0x6e,0x74,0x20,0x6d,0x61,
330
        0x74,0x63,0x68,0x65,0x73,0x20,0x6f,0x66,
331
        0x20,0x73,0x70,0x6c,0x69,0x74,0x74,0x65,
332
        0x64,0x20,0x70,0x61,0x74,0x74,0x65,0x72,
333
        0x6e,0x73,0x20,0x62,0x65,0x74,0x77,0x65,
334
        0x65,0x6e,0x20,0x6d,0x75,0x6c,0x74,0x69,
335
        0x70,0x6c,0x65,0x20,0x63,0x68,0x75,0x6e,
336
        0x6b,0x73,0x21 }; /* end raw_eth_pkt */
337
    uint8_t p[sizeof(raw_eth_pkt)];
338
    uint16_t psize = sizeof(raw_eth_pkt);
339

340
    /* would be unittest */
341
    int run_mode_backup = SCRunmodeGet();
342
    SCRunmodeSet(RUNMODE_NFQ);
343
    ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
344
                             sig, sid, p, &psize);
345
    if (ret == 1) {
346
        SCLogDebug("replace: test1 phase1");
347
        ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL);
348
    }
349
    SCRunmodeSet(run_mode_backup);
350
    return ret;
351
}
352

353

354
/**
355
 * \brief Wrapper for DetectContentLongPatternMatchTest
356
 */
357
static int DetectReplaceLongPatternMatchTestUDPWrp(const char *sig, uint32_t sid, const char *sig_rep,  uint32_t sid_rep)
358
{
359
    int ret;
360
    /** Real UDP DNS packet with a request A to a1.twimg.com
361
     */
362
    uint8_t raw_eth_pkt[] = {
363
        0x8c, 0xa9, 0x82, 0x75, 0x5d, 0x62, 0xb4, 0x07, 
364
        0xf9, 0xf3, 0xc7, 0x0a, 0x08, 0x00, 0x45, 0x00, 
365
        0x00, 0x3a, 0x92, 0x4f, 0x40, 0x00, 0x40, 0x11, 
366
        0x31, 0x1a, 0xc0, 0xa8, 0x00, 0x02, 0xc1, 0xbd, 
367
        0xf4, 0xe1, 0x3b, 0x7e, 0x00, 0x35, 0x00, 0x26, 
368
        0xcb, 0x81, 0x37, 0x62, 0x01, 0x00, 0x00, 0x01, 
369
        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x61, 
370
        0x31, 0x05, 0x74, 0x77, 0x69, 0x6d, 0x67, 0x03, 
371
        0x63, 0x6f, 0x6d, 0x00, 0x00, 0x01, 0x00, 0x01 };
372

373
    uint8_t p[sizeof(raw_eth_pkt)];
374
    uint16_t psize = sizeof(raw_eth_pkt);
375

376
    int run_mode_backup = SCRunmodeGet();
377
    SCRunmodeSet(RUNMODE_NFQ);
378
    ret = DetectReplaceLongPatternMatchTest(raw_eth_pkt, (uint16_t)sizeof(raw_eth_pkt),
379
                             sig, sid, p, &psize);
380
    if (ret == 1) {
381
        SCLogDebug("replace: test1 phase1 ok: %" PRIuMAX" vs %d",(uintmax_t)sizeof(raw_eth_pkt),psize);
382
        ret = DetectReplaceLongPatternMatchTest(p, psize, sig_rep, sid_rep, NULL, NULL);
383
    }
384
    SCRunmodeSet(run_mode_backup);
385
    return ret;
386
}
387

388
/**
389
 * \test Check if replace is working
390
 */
391
static int DetectReplaceMatchTest01(void)
392
{
393
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
394
                " content:\"big\"; replace:\"pig\"; sid:1;)";
395
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
396
                " content:\"this is a pig test\"; sid:2;)";
397
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
398
    PASS;
399
}
400

401
/**
402
 * \test Check if replace is working with offset
403
 */
404
static int DetectReplaceMatchTest02(void)
405
{
406
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
407
                " content:\"th\"; offset: 4; replace:\"TH\"; sid:1;)";
408
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
409
                " content:\"THis\"; offset:4; sid:2;)";
410
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
411
    PASS;
412
}
413

414
/**
415
 * \test Check if replace is working with offset and keyword inversion
416
 */
417
static int DetectReplaceMatchTest03(void)
418
{
419
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
420
                " content:\"th\"; replace:\"TH\"; offset: 4; sid:1;)";
421
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
422
                " content:\"THis\"; offset:4; sid:2;)";
423
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
424
    PASS;
425
}
426

427
/**
428
 * \test Check if replace is working with second content
429
 */
430
static int DetectReplaceMatchTest04(void)
431
{
432
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
433
                " content:\"th\"; replace:\"TH\"; content:\"patter\"; replace:\"matter\"; sid:1;)";
434
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
435
                " content:\"THis\"; content:\"matterns\"; sid:2;)";
436
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
437
    PASS;
438
}
439

440
/**
441
 * \test Check if replace is not done when second content don't match
442
 */
443
static int DetectReplaceMatchTest05(void)
444
{
445
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
446
                " content:\"th\"; replace:\"TH\"; content:\"nutella\"; sid:1;)";
447
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
448
                " content:\"TH\"; sid:2;)";
449
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
450
    PASS;
451
}
452

453
/**
454
 * \test Check if replace is not done when second content match and not
455
 * first
456
 */
457
static int DetectReplaceMatchTest06(void)
458
{
459
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
460
                " content:\"nutella\"; replace:\"commode\"; content:\"this is\"; sid:1;)";
461
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
462
                " content:\"commode\"; sid:2;)";
463
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
464
    PASS;
465
}
466

467
/**
468
 * \test Check if replace is working when nocase used
469
 */
470
static int DetectReplaceMatchTest07(void)
471
{
472
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
473
                " content:\"BiG\"; nocase; replace:\"pig\"; sid:1;)";
474
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
475
                " content:\"this is a pig test\"; sid:2;)";
476
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
477
    PASS;
478
}
479

480
/**
481
 * \test Check if replace is working when depth is used
482
 */
483
static int DetectReplaceMatchTest08(void)
484
{
485
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
486
                " content:\"big\"; depth:17; replace:\"pig\"; sid:1;)";
487
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
488
                " content:\"this is a pig test\"; sid:2;)";
489
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
490
    PASS;
491
}
492

493
/**
494
 * \test Check if replace is working when depth block match used
495
 */
496
static int DetectReplaceMatchTest09(void)
497
{
498
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
499
                " content:\"big\"; depth:16; replace:\"pig\"; sid:1;)";
500
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
501
                " content:\"this is a pig test\"; sid:2;)";
502
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
503
    PASS;
504
}
505

506
/**
507
 * \test Check if replace is working when depth block match used
508
 */
509
static int DetectReplaceMatchTest10(void)
510
{
511
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
512
                " content:\"big\"; depth:17; replace:\"pig\"; offset: 14; sid:1;)";
513
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
514
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
515
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
516
    PASS;
517
}
518

519
/**
520
 * \test Check if replace is working with within
521
 */
522
static int DetectReplaceMatchTest11(void)
523
{
524
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
525
                " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 11; sid:1;)";
526
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
527
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
528

529
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
530
    PASS;
531
}
532

533
/**
534
 * \test Check if replace is working with within
535
 */
536
static int DetectReplaceMatchTest12(void)
537
{
538
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
539
                " content:\"big\"; replace:\"pig\"; content:\"to\"; within: 4; sid:1;)";
540
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
541
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
542
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
543
    PASS;
544
}
545

546
/**
547
 * \test Check if replace is working with within
548
 */
549
static int DetectReplaceMatchTest13(void)
550
{
551
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
552
                " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 1; sid:1;)";
553
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
554
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
555
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
556
    PASS;
557
}
558

559
/**
560
 * \test Check if replace is working with within
561
 */
562
static int DetectReplaceMatchTest14(void)
563
{
564
    const char *sig = "alert tcp any any -> any any (msg:\"Nothing..\";"
565
                " content:\"big\"; replace:\"pig\"; content:\"test\"; distance: 2; sid:1;)";
566
    const char *sig_rep = "alert tcp any any -> any any (msg:\"replace worked\";"
567
                " content:\"pig\"; depth:17; offset:14; sid:2;)";
568
    FAIL_IF(DetectReplaceLongPatternMatchTestWrp(sig, 1, sig_rep, 2));
569
    PASS;
570
}
571

572
/**
573
 * \test Check if replace is working with within
574
 */
575
static int DetectReplaceMatchTest15(void)
576
{
577
    const char *sig = "alert udp any any -> any any (msg:\"Nothing..\";"
578
                " content:\"com\"; replace:\"org\"; sid:1;)";
579
    const char *sig_rep = "alert udp any any -> any any (msg:\"replace worked\";"
580
                " content:\"twimg|03|org\"; sid:2;)";
581
    FAIL_IF_NOT(DetectReplaceLongPatternMatchTestUDPWrp(sig, 1, sig_rep, 2));
582
    PASS;
583
}
584

585

586
/**
587
 * \test Parsing test
588
 */
589
static int DetectReplaceParseTest01(void)
590
{
591
    int run_mode_backup = SCRunmodeGet();
592
    SCRunmodeSet(RUNMODE_NFQ);
593

594
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
595
    FAIL_IF_NULL(de_ctx);
596

597
    de_ctx->flags |= DE_QUIET;
598
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
599
            "alert udp any any -> any any "
600
            "(msg:\"test\"; content:\"doh\"; replace:\"; sid:238012;)"));
601

602
    SCRunmodeSet(run_mode_backup);
603
    DetectEngineCtxFree(de_ctx);
604
    PASS;
605
}
606

607
/**
608
 * \test Parsing test: non valid because of http protocol
609
 */
610
static int DetectReplaceParseTest02(void)
611
{
612
    int run_mode_backup = SCRunmodeGet();
613
    SCRunmodeSet(RUNMODE_NFQ);
614

615
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
616
    FAIL_IF_NULL(de_ctx);
617

618
    de_ctx->flags |= DE_QUIET;
619
    FAIL_IF_NULL(DetectEngineAppendSig(de_ctx,
620
            "alert http any any -> any any "
621
            "(msg:\"test\"; content:\"doh\"; replace:\"bon\"; sid:238012;)"));
622

623
    SCRunmodeSet(run_mode_backup);
624
    DetectEngineCtxFree(de_ctx);
625
    PASS;
626
}
627

628
/**
629
 * \test Parsing test: non valid because of http_header on same content
630
 * as replace keyword
631
 */
632
static int DetectReplaceParseTest03(void)
633
{
634
    int run_mode_backup = SCRunmodeGet();
635
    SCRunmodeSet(RUNMODE_NFQ);
636

637
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
638

639
    FAIL_IF_NULL(de_ctx);
640

641
    de_ctx->flags |= DE_QUIET;
642
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
643
            "alert tcp any any -> any any "
644
            "(msg:\"test\"; content:\"doh\"; replace:\"don\"; http_header; sid:238012;)"));
645

646
    SCRunmodeSet(run_mode_backup);
647
    DetectEngineCtxFree(de_ctx);
648
    PASS;
649
}
650

651
/**
652
 * \test Parsing test no content
653
 */
654
static int DetectReplaceParseTest04(void)
655
{
656
    int run_mode_backup = SCRunmodeGet();
657
    SCRunmodeSet(RUNMODE_NFQ);
658

659
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
660
    FAIL_IF_NULL(de_ctx);
661

662
    de_ctx->flags |= DE_QUIET;
663
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
664
                                                   "(msg:\"test\"; replace:\"don\"; sid:238012;)"));
665

666
    SCRunmodeSet(run_mode_backup);
667
    DetectEngineCtxFree(de_ctx);
668
    PASS;
669
}
670

671
/**
672
 * \test Parsing test content after replace
673
 */
674
static int DetectReplaceParseTest05(void)
675
{
676
    int run_mode_backup = SCRunmodeGet();
677
    SCRunmodeSet(RUNMODE_NFQ);
678

679
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
680
    FAIL_IF_NULL(de_ctx);
681

682
    de_ctx->flags |= DE_QUIET;
683
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
684
            "alert tcp any any -> any any "
685
            "(msg:\"test\"; replace:\"don\"; content:\"doh\"; sid:238012;)"));
686

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

692
/**
693
 * \test Parsing test content and replace length differ
694
 */
695
static int DetectReplaceParseTest06(void)
696
{
697
    int run_mode_backup = SCRunmodeGet();
698
    SCRunmodeSet(RUNMODE_NFQ);
699

700
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
701
    FAIL_IF_NULL(de_ctx);
702

703
    de_ctx->flags |= DE_QUIET;
704
    FAIL_IF_NOT_NULL(DetectEngineAppendSig(de_ctx,
705
            "alert tcp any any -> any any "
706
            "(msg:\"test\"; content:\"don\"; replace:\"donut\"; sid:238012;)"));
707

708
    SCRunmodeSet(run_mode_backup);
709
    DetectEngineCtxFree(de_ctx);
710
    PASS;
711
}
712

713
/**
714
 * \test Parsing test content and replace length differ
715
 */
716
static int DetectReplaceParseTest07(void)
717
{
718
    int run_mode_backup = SCRunmodeGet();
719
    SCRunmodeSet(RUNMODE_NFQ);
720

721
    DetectEngineCtx *de_ctx = DetectEngineCtxInit();
722
    FAIL_IF_NULL(de_ctx);
723

724
    de_ctx->flags |= DE_QUIET;
725
    FAIL_IF_NOT_NULL(
726
            DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any "
727
                                          "(msg:\"test\"; content:\"don\"; replace:\"dou\"; "
728
                                          "content:\"jpg\"; http_header; sid:238012;)"));
729

730
    SCRunmodeSet(run_mode_backup);
731
    DetectEngineCtxFree(de_ctx);
732
    PASS;
733
}
734

735
/**
736
 * \brief this function registers unit tests for DetectContent
737
 */
738
void DetectReplaceRegisterTests(void)
739
{
740
/* matching */
741
    UtRegisterTest("DetectReplaceMatchTest01", DetectReplaceMatchTest01);
742
    UtRegisterTest("DetectReplaceMatchTest02", DetectReplaceMatchTest02);
743
    UtRegisterTest("DetectReplaceMatchTest03", DetectReplaceMatchTest03);
744
    UtRegisterTest("DetectReplaceMatchTest04", DetectReplaceMatchTest04);
745
    UtRegisterTest("DetectReplaceMatchTest05", DetectReplaceMatchTest05);
746
    UtRegisterTest("DetectReplaceMatchTest06", DetectReplaceMatchTest06);
747
    UtRegisterTest("DetectReplaceMatchTest07", DetectReplaceMatchTest07);
748
    UtRegisterTest("DetectReplaceMatchTest08", DetectReplaceMatchTest08);
749
    UtRegisterTest("DetectReplaceMatchTest09", DetectReplaceMatchTest09);
750
    UtRegisterTest("DetectReplaceMatchTest10", DetectReplaceMatchTest10);
751
    UtRegisterTest("DetectReplaceMatchTest11", DetectReplaceMatchTest11);
752
    UtRegisterTest("DetectReplaceMatchTest12", DetectReplaceMatchTest12);
753
    UtRegisterTest("DetectReplaceMatchTest13", DetectReplaceMatchTest13);
754
    UtRegisterTest("DetectReplaceMatchTest14", DetectReplaceMatchTest14);
755
    UtRegisterTest("DetectReplaceMatchTest15", DetectReplaceMatchTest15);
756
/* parsing */
757
    UtRegisterTest("DetectReplaceParseTest01", DetectReplaceParseTest01);
758
    UtRegisterTest("DetectReplaceParseTest02", DetectReplaceParseTest02);
759
    UtRegisterTest("DetectReplaceParseTest03", DetectReplaceParseTest03);
760
    UtRegisterTest("DetectReplaceParseTest04", DetectReplaceParseTest04);
761
    UtRegisterTest("DetectReplaceParseTest05", DetectReplaceParseTest05);
762
    UtRegisterTest("DetectReplaceParseTest06", DetectReplaceParseTest06);
763
    UtRegisterTest("DetectReplaceParseTest07", DetectReplaceParseTest07);
764
}
765
#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