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

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

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

89.83
/rust/src/asn1/parse_rules.rs
1
/* Copyright (C) 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
use nom8::branch::alt;
19
use nom8::bytes::complete::tag;
20
use nom8::character::complete::{digit1, multispace0, multispace1};
21
use nom8::combinator::{map_res, opt, verify};
22
use nom8::error::{make_error, ErrorKind};
23
use nom8::sequence::separated_pair;
24
use nom8::{Err, IResult, Parser};
25
use std::ffi::CStr;
26
use std::os::raw::c_char;
27

28
const ASN1_DEFAULT_MAX_FRAMES: u16 = 30;
29

30
/// Parse the asn1 keyword and return a pointer to a `DetectAsn1Data`
31
/// containing the parsed options, returns null on failure
32
///
33
/// # Safety
34
///
35
/// pointer must be free'd using `SCAsn1DetectFree`
36
#[no_mangle]
37
pub unsafe extern "C" fn SCAsn1DetectParse(input: *const c_char) -> *mut DetectAsn1Data {
1,269✔
38
    if input.is_null() {
1,269✔
39
        return std::ptr::null_mut();
×
40
    }
1,269✔
41

42
    let arg = match CStr::from_ptr(input).to_str() {
1,269✔
43
        Ok(arg) => arg,
1,269✔
44
        _ => {
45
            return std::ptr::null_mut();
×
46
        }
47
    };
48

49
    match asn1_parse_rule(arg) {
1,269✔
50
        Ok((_rest, data)) => {
356✔
51
            let mut data = data;
356✔
52

53
            // Get configuration value
54
            if let Some(max_frames) = crate::conf::conf_get("asn1-max-frames") {
356✔
UNCOV
55
                if let Ok(v) = max_frames.parse::<u16>() {
×
UNCOV
56
                    data.max_frames = v;
×
UNCOV
57
                } else {
×
58
                    SCLogError!("Could not parse asn1-max-frames: {}", max_frames);
×
59
                    return std::ptr::null_mut();
×
60
                };
61
            }
356✔
62

63
            Box::into_raw(Box::new(data))
356✔
64
        }
65
        Err(e) => {
913✔
66
            SCLogError!("Malformed asn1 argument: {}", e.to_string());
913✔
67
            std::ptr::null_mut()
913✔
68
        }
69
    }
70
}
1,269✔
71

72
/// Free a `DetectAsn1Data` object allocated by Rust
73
///
74
/// # Safety
75
///
76
/// ptr must be a valid object obtained using `SCAsn1DetectParse`
77
#[no_mangle]
78
pub unsafe extern "C" fn SCAsn1DetectFree(ptr: *mut DetectAsn1Data) {
356✔
79
    if ptr.is_null() {
356✔
80
        return;
×
81
    }
356✔
82
    drop(Box::from_raw(ptr));
356✔
83
}
356✔
84

85
/// Struct to hold parsed asn1 keyword options
86
#[derive(Debug, PartialEq, Eq)]
87
pub struct DetectAsn1Data {
88
    pub bitstring_overflow: bool,
89
    pub double_overflow: bool,
90
    pub oversize_length: Option<u32>,
91
    pub absolute_offset: Option<u16>,
92
    pub relative_offset: Option<i32>,
93
    pub max_frames: u16,
94
}
95

96
impl Default for DetectAsn1Data {
97
    fn default() -> DetectAsn1Data {
1,269✔
98
        DetectAsn1Data {
1,269✔
99
            bitstring_overflow: false,
1,269✔
100
            double_overflow: false,
1,269✔
101
            oversize_length: None,
1,269✔
102
            absolute_offset: None,
1,269✔
103
            relative_offset: None,
1,269✔
104
            max_frames: ASN1_DEFAULT_MAX_FRAMES,
1,269✔
105
        }
1,269✔
106
    }
1,269✔
107
}
108

109
fn parse_u32_number(input: &str) -> IResult<&str, u32> {
297✔
110
    map_res(digit1, |digits: &str| digits.parse::<u32>()).parse(input)
297✔
111
}
297✔
112

113
fn parse_u16_number(input: &str) -> IResult<&str, u16> {
703✔
114
    map_res(digit1, |digits: &str| digits.parse::<u16>()).parse(input)
703✔
115
}
703✔
116

117
fn parse_i32_number(input: &str) -> IResult<&str, i32> {
510✔
118
    let (rest, negate) = opt(tag("-")).parse(input)?;
510✔
119
    let (rest, d) = map_res(digit1, |s: &str| s.parse::<i32>()).parse(rest)?;
510✔
120
    let n = if negate.is_some() { -1 } else { 1 };
496✔
121
    Ok((rest, d * n))
496✔
122
}
510✔
123

124
/// Parse asn1 keyword options
125
pub(super) fn asn1_parse_rule(input: &str) -> IResult<&str, DetectAsn1Data> {
1,269✔
126
    // If nothing to parse, return
1,269✔
127
    if input.is_empty() {
1,269✔
UNCOV
128
        return Err(Err::Error(make_error(
×
UNCOV
129
            input,
×
UNCOV
130
            ErrorKind::Eof,
×
UNCOV
131
        )));
×
132
    }
1,269✔
133

134
    // Rule parsing functions
135
    fn bitstring_overflow(i: &str) -> IResult<&str, &str> {
3,132✔
136
        tag("bitstring_overflow").parse(i)
3,132✔
137
    }
3,132✔
138

139
    fn double_overflow(i: &str) -> IResult<&str, &str> {
3,132✔
140
        tag("double_overflow").parse(i)
3,132✔
141
    }
3,132✔
142

143
    fn oversize_length(i: &str) -> IResult<&str, (&str, u32)> {
3,132✔
144
        separated_pair(tag("oversize_length"), multispace1, parse_u32_number).parse(i)
3,132✔
145
    }
3,132✔
146

147
    fn absolute_offset(i: &str) -> IResult<&str, (&str, u16)> {
3,132✔
148
        separated_pair(tag("absolute_offset"), multispace1, parse_u16_number).parse(i)
3,132✔
149
    }
3,132✔
150

151
    fn relative_offset(i: &str) -> IResult<&str, (&str, i32)> {
3,132✔
152
        separated_pair(
3,132✔
153
            tag("relative_offset"),
3,132✔
154
            multispace1,
3,132✔
155
            verify(parse_i32_number, |v| {
3,132✔
156
                *v >= -i32::from(u16::MAX) && *v <= i32::from(u16::MAX)
496✔
157
            }),
3,132✔
158
        ).parse(i)
3,132✔
159
    }
3,132✔
160

161
    let mut data = DetectAsn1Data::default();
1,269✔
162

1,269✔
163
    let mut rest = input;
1,269✔
164

165
    // Parse the input and set data
166
    while !rest.is_empty() {
3,488✔
167
        let (
168
            new_rest,
3,132✔
169
            (
3,132✔
170
                _,
3,132✔
171
                bitstring_overflow,
3,132✔
172
                double_overflow,
3,132✔
173
                oversize_length,
3,132✔
174
                absolute_offset,
3,132✔
175
                relative_offset,
3,132✔
176
                _,
177
            ),
178
        ) = (
3,132✔
179
            opt(multispace0),
3,132✔
180
            opt(bitstring_overflow),
3,132✔
181
            opt(double_overflow),
3,132✔
182
            opt(oversize_length),
3,132✔
183
            opt(absolute_offset),
3,132✔
184
            opt(relative_offset),
3,132✔
185
            opt(alt((multispace1, tag(",")))),
3,132✔
186
        ).parse(rest)?;
3,132✔
187

188
        if bitstring_overflow.is_some() {
3,132✔
189
            data.bitstring_overflow = true;
391✔
190
        } else if double_overflow.is_some() {
2,741✔
191
            data.double_overflow = true;
360✔
192
        } else if let Some((_, v)) = oversize_length {
2,381✔
193
            data.oversize_length = Some(v);
284✔
194
        } else if let Some((_, v)) = absolute_offset {
2,097✔
195
            data.absolute_offset = Some(v);
693✔
196
        } else if let Some((_, v)) = relative_offset {
1,404✔
197
            data.relative_offset = Some(v);
491✔
198
        } else {
491✔
199
            return Err(Err::Error(make_error(
913✔
200
                rest,
913✔
201
                ErrorKind::Verify,
913✔
202
            )));
913✔
203
        }
204

205
        rest = new_rest;
2,219✔
206
    }
207

208
    Ok((rest, data))
356✔
209
}
1,269✔
210

211
#[cfg(test)]
212
mod tests {
213
    use super::*;
214
    use test_case::test_case;
215

216
    // Test oversize_length
217
    #[test_case("oversize_length 1024",
218
        DetectAsn1Data { oversize_length: Some(1024), ..Default::default()};
219
        "check that we parse oversize_length correctly")]
220
    #[test_case("oversize_length 0",
221
        DetectAsn1Data { oversize_length: Some(0), ..Default::default()};
222
        "check lower bound on oversize_length")]
223
    #[test_case("oversize_length -1",
224
        DetectAsn1Data::default() => panics r#"Error { input: "oversize_length -1", code: Verify }"#;
225
        "check under lower bound on oversize_length")]
226
    #[test_case("oversize_length 4294967295",
227
        DetectAsn1Data { oversize_length: Some(4294967295), ..Default::default()};
228
        "check upper bound on oversize_length")]
229
    #[test_case("oversize_length 4294967296",
230
        DetectAsn1Data::default() => panics r#"Error { input: "oversize_length 4294967296", code: Verify }"#;
231
        "check over upper bound on oversize_length")]
232
    #[test_case("oversize_length",
233
        DetectAsn1Data::default() => panics r#"Error { input: "oversize_length", code: Verify }"#;
234
        "check that we fail if the needed arg oversize_length is not given")]
235
    // Test absolute_offset
236
    #[test_case("absolute_offset 1024",
237
        DetectAsn1Data { absolute_offset: Some(1024), ..Default::default()};
238
        "check that we parse absolute_offset correctly")]
239
    #[test_case("absolute_offset 0",
240
        DetectAsn1Data { absolute_offset: Some(0), ..Default::default()};
241
        "check lower bound on absolute_offset")]
242
    #[test_case("absolute_offset -1",
243
        DetectAsn1Data::default() => panics r#"Error { input: "absolute_offset -1", code: Verify }"#;
244
        "check under lower bound on absolute_offset")]
245
    #[test_case("absolute_offset 65535",
246
        DetectAsn1Data { absolute_offset: Some(65535), ..Default::default()};
247
        "check upper bound on absolute_offset")]
248
    #[test_case("absolute_offset 65536",
249
        DetectAsn1Data::default() => panics r#"Error { input: "absolute_offset 65536", code: Verify }"#;
250
        "check over upper bound on absolute_offset")]
251
    #[test_case("absolute_offset",
252
        DetectAsn1Data::default() => panics r#"Error { input: "absolute_offset", code: Verify }"#;
253
        "check that we fail if the needed arg absolute_offset is not given")]
254
    // Test relative_offset
255
    #[test_case("relative_offset 1024",
256
        DetectAsn1Data { relative_offset: Some(1024), ..Default::default()};
257
        "check that we parse relative_offset correctly")]
258
    #[test_case("relative_offset -65535",
259
        DetectAsn1Data { relative_offset: Some(-65535), ..Default::default()};
260
        "check lower bound on relative_offset")]
261
    #[test_case("relative_offset -65536",
262
        DetectAsn1Data::default() => panics r#"Error { input: "relative_offset -65536", code: Verify }"#;
263
        "check under lower bound on relative_offset")]
264
    #[test_case("relative_offset 65535",
265
        DetectAsn1Data { relative_offset: Some(65535), ..Default::default()};
266
        "check upper bound on relative_offset")]
267
    #[test_case("relative_offset 65536",
268
        DetectAsn1Data::default() => panics r#"Error { input: "relative_offset 65536", code: Verify }"#;
269
        "check over upper bound on relative_offset")]
270
    #[test_case("relative_offset",
271
        DetectAsn1Data::default() => panics r#"Error { input: "relative_offset", code: Verify }"#;
272
        "check that we fail if the needed arg relative_offset is not given")]
273
    // Test bitstring_overflow
274
    #[test_case("bitstring_overflow",
275
        DetectAsn1Data { bitstring_overflow: true, ..Default::default()};
276
        "check that we parse bitstring_overflow correctly")]
277
    // Test double_overflow
278
    #[test_case("double_overflow",
279
        DetectAsn1Data { double_overflow: true, ..Default::default()};
280
        "check that we parse double_overflow correctly")]
281
    // Test combination of params
282
    #[test_case("oversize_length 1024, relative_offset 10",
283
        DetectAsn1Data { oversize_length: Some(1024), relative_offset: Some(10),
284
            ..Default::default()};
285
        "check for combinations of keywords (comma seperated)")]
286
    #[test_case("oversize_length 1024 absolute_offset 10",
287
        DetectAsn1Data { oversize_length: Some(1024), absolute_offset: Some(10),
288
            ..Default::default()};
289
        "check for combinations of keywords (space seperated)")]
290
    #[test_case("oversize_length 1024 absolute_offset 10, bitstring_overflow",
291
        DetectAsn1Data { bitstring_overflow: true, oversize_length: Some(1024),
292
            absolute_offset: Some(10), ..Default::default()};
293
        "check for combinations of keywords (space/comma seperated)")]
294
    #[test_case(
295
        "double_overflow, oversize_length 1024 absolute_offset 10,\n bitstring_overflow",
296
        DetectAsn1Data { double_overflow: true, bitstring_overflow: true,
297
            oversize_length: Some(1024), absolute_offset: Some(10),
298
            ..Default::default()};
299
        "1. check for combinations of keywords (space/comma/newline seperated)")]
300
    #[test_case(
301
        "\n\t double_overflow, oversize_length 1024 relative_offset 10,\n bitstring_overflow",
302
        DetectAsn1Data { double_overflow: true, bitstring_overflow: true,
303
            oversize_length: Some(1024), relative_offset: Some(10),
304
            ..Default::default()};
305
        "2. check for combinations of keywords (space/comma/newline seperated)")]
306
    // Test empty
307
    #[test_case("",
308
        DetectAsn1Data::default() => panics r#"Error { input: "", code: Eof }"#;
309
        "test that we break with a empty string")]
310
    // Test invalid rules
311
    #[test_case("oversize_length 1024, some_other_param 360",
312
        DetectAsn1Data::default() => panics r#"Error { input: " some_other_param 360", code: Verify }"#;
313
        "test that we break on invalid options")]
314
    #[test_case("oversize_length 1024,,",
315
        DetectAsn1Data::default() => panics r#"Error { input: ",", code: Verify }"#;
316
        "test that we break on invalid format (missing option)")]
317
    #[test_case("bitstring_overflowabsolute_offset",
318
        DetectAsn1Data::default() => panics r#"Error { input: "absolute_offset", code: Verify }"#;
319
        "test that we break on invalid format (missing separator)")]
320
    fn test_asn1_parse_rule(input: &str, expected: DetectAsn1Data) {
321
        let (rest, res) = asn1_parse_rule(input).unwrap();
322

323
        assert_eq!(0, rest.len());
324
        assert_eq!(expected, res);
325
    }
326
}
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