Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/detect-transform-compress-whitespace.c
Line
Count
Source
1
/* Copyright (C) 2007-2020 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17
18
/**
19
 * \file
20
 *
21
 * \author Victor Julien <victor@inliniac.net>
22
 *
23
 * Implements the compress_whitespace transform keyword
24
 */
25
26
#include "suricata-common.h"
27
28
#include "detect.h"
29
#include "detect-engine.h"
30
#include "detect-engine-prefilter.h"
31
#include "detect-parse.h"
32
#include "detect-transform-compress-whitespace.h"
33
34
#include "util-unittest.h"
35
#include "util-print.h"
36
37
static int DetectTransformCompressWhitespaceSetup (DetectEngineCtx *, Signature *, const char *);
38
#ifdef UNITTESTS
39
static void DetectTransformCompressWhitespaceRegisterTests(void);
40
#endif
41
static void TransformCompressWhitespace(InspectionBuffer *buffer, void *options);
42
static bool TransformCompressWhitespaceValidate(
43
        const uint8_t *content, uint16_t content_len, void *options);
44
45
void DetectTransformCompressWhitespaceRegister(void)
46
34
{
47
34
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].name = "compress_whitespace";
48
34
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].desc =
49
34
        "modify buffer to compress consecutive whitespace characters "
50
34
        "into a single one before inspection";
51
34
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].url =
52
34
        "/rules/transforms.html#compress-whitespace";
53
34
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].Transform =
54
34
        TransformCompressWhitespace;
55
34
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].TransformValidate =
56
34
            TransformCompressWhitespaceValidate;
57
34
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].Setup =
58
34
        DetectTransformCompressWhitespaceSetup;
59
#ifdef UNITTESTS
60
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].RegisterTests =
61
        DetectTransformCompressWhitespaceRegisterTests;
62
#endif
63
34
    sigmatch_table[DETECT_TRANSFORM_COMPRESS_WHITESPACE].flags |= SIGMATCH_NOOPT;
64
34
}
65
66
/**
67
 *  \internal
68
 *  \brief Apply the compress_whitespace keyword to the last pattern match
69
 *  \param det_ctx detection engine ctx
70
 *  \param s signature
71
 *  \param nullstr should be null
72
 *  \retval 0 ok
73
 *  \retval -1 failure
74
 */
75
static int DetectTransformCompressWhitespaceSetup (DetectEngineCtx *de_ctx, Signature *s, const char *nullstr)
76
704
{
77
704
    SCEnter();
78
704
    int r = DetectSignatureAddTransform(s, DETECT_TRANSFORM_COMPRESS_WHITESPACE, NULL);
79
704
    SCReturnInt(r);
80
704
}
81
82
/*
83
 *  \brief Validate content bytes to see if it's compatible with this transform
84
 *  \param content Byte array to check for compatibility
85
 *  \param content_len Number of bytes to check
86
 *  \param options Ignored
87
 *  \retval false If the string contains spaces
88
 *  \retval true Otherwise.
89
 */
90
static bool TransformCompressWhitespaceValidate(
91
        const uint8_t *content, uint16_t content_len, void *options)
92
571
{
93
571
    if (content) {
94
7.86k
        for (uint32_t i = 0; i < content_len; i++) {
95
7.30k
            if (!isspace(*content++)) {
96
6.50k
                continue;
97
6.50k
            }
98
797
            if ((i + 1) < content_len && isspace(*content)) {
99
9
                return false;
100
9
            }
101
797
        }
102
571
    }
103
562
    return true;
104
571
}
105
106
static void TransformCompressWhitespace(InspectionBuffer *buffer, void *options)
107
735
{
108
735
    const uint8_t *input = buffer->inspect;
109
735
    const uint32_t input_len = buffer->inspect_len;
110
735
    if (input_len == 0) {
111
0
        return;
112
0
    }
113
114
    // we can only shrink
115
735
    uint8_t *output = InspectionBufferCheckAndExpand(buffer, input_len);
116
735
    if (output == NULL) {
117
0
        return;
118
0
    }
119
735
    uint8_t *oi = output, *os = output;
120
121
    //PrintRawDataFp(stdout, input, input_len);
122
392k
    for (uint32_t i = 0; i < input_len; ) {
123
391k
        if (!(isspace(*input))) {
124
378k
            *oi++ = *input++;
125
378k
            i++;
126
378k
        } else {
127
12.9k
            *oi++ = *input++;
128
12.9k
            i++;
129
130
19.1k
            while (i < input_len && isspace(*input)) {
131
6.14k
                input++;
132
6.14k
                i++;
133
6.14k
            }
134
12.9k
        }
135
391k
    }
136
735
    uint32_t output_size = oi - os;
137
    //PrintRawDataFp(stdout, output, output_size);
138
139
735
    InspectionBufferTruncate(buffer, output_size);
140
735
}
141
142
#ifdef UNITTESTS
143
static int TransformDoubleWhitespace(InspectionBuffer *buffer)
144
{
145
    const uint8_t *input = buffer->inspect;
146
    const uint32_t input_len = buffer->inspect_len;
147
    uint8_t output[input_len * 2]; // if all chars are whitespace this fits
148
    memset(&output, 0, input_len * 2);
149
    uint8_t *oi = output, *os = output;
150
151
    PrintRawDataFp(stdout, input, input_len);
152
    for (uint32_t i = 0; i < input_len; i++) {
153
        if (isspace(*input)) {
154
            *oi++ = *input;
155
        }
156
        *oi++ = *input;
157
        input++;
158
    }
159
    uint32_t output_size = oi - os;
160
    PrintRawDataFp(stdout, output, output_size);
161
162
    InspectionBufferCopy(buffer, os, output_size);
163
    return 0;
164
}
165
166
static int DetectTransformCompressWhitespaceTest01(void)
167
{
168
    const uint8_t *input = (const uint8_t *)" A B C D ";
169
    uint32_t input_len = strlen((char *)input);
170
171
    InspectionBuffer buffer;
172
    InspectionBufferInit(&buffer, 9);
173
    InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
174
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
175
    TransformCompressWhitespace(&buffer, NULL);
176
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
177
    InspectionBufferFree(&buffer);
178
    PASS;
179
}
180
181
static int DetectTransformCompressWhitespaceTest02(void)
182
{
183
    const uint8_t *input = (const uint8_t *)" A B C D ";
184
    uint32_t input_len = strlen((char *)input);
185
186
    InspectionBuffer buffer;
187
    InspectionBufferInit(&buffer, 9);
188
    InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
189
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
190
    TransformDoubleWhitespace(&buffer);
191
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
192
    TransformDoubleWhitespace(&buffer);
193
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
194
    TransformCompressWhitespace(&buffer, NULL);
195
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
196
    InspectionBufferFree(&buffer);
197
    PASS;
198
}
199
200
static int DetectTransformCompressWhitespaceTest03(void)
201
{
202
    const uint8_t *input = (const uint8_t *)" A B C D  ";
203
    uint32_t input_len = strlen((char *)input);
204
205
    InspectionBuffer buffer;
206
    InspectionBufferInit(&buffer, 10);
207
    InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
208
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
209
    FAIL_IF(TransformCompressWhitespaceValidate(buffer.inspect, buffer.inspect_len, NULL));
210
    PASS;
211
}
212
213
static int DetectTransformCompressWhitespaceTest04(void)
214
{
215
    const uint8_t *input = (const uint8_t *)" A B C D ";
216
    uint32_t input_len = strlen((char *)input);
217
218
    InspectionBuffer buffer;
219
    InspectionBufferInit(&buffer, 9);
220
    InspectionBufferSetup(NULL, -1, &buffer, input, input_len);
221
    TransformDoubleWhitespace(&buffer);
222
    PrintRawDataFp(stdout, buffer.inspect, buffer.inspect_len);
223
    FAIL_IF(TransformCompressWhitespaceValidate(buffer.inspect, buffer.inspect_len, NULL));
224
    PASS;
225
}
226
227
static void DetectTransformCompressWhitespaceRegisterTests(void)
228
{
229
    UtRegisterTest("DetectTransformCompressWhitespaceTest01",
230
            DetectTransformCompressWhitespaceTest01);
231
    UtRegisterTest("DetectTransformCompressWhitespaceTest02",
232
            DetectTransformCompressWhitespaceTest02);
233
    UtRegisterTest(
234
            "DetectTransformCompressWhitespaceTest03", DetectTransformCompressWhitespaceTest03);
235
    UtRegisterTest(
236
            "DetectTransformCompressWhitespaceTest04", DetectTransformCompressWhitespaceTest04);
237
}
238
#endif