use std::convert::{From, TryFrom};
use std::ops::Range;
use std::str::{self, FromStr};
use crate::constants::*;
use crate::types::*;
use nom::{
branch::*, bytes::complete::*, character::complete::*, character::*, combinator::*, multi::*,
sequence::*, IResult, Offset,
};
#[allow(clippy::type_complexity)]
pub fn parse(
mut raw: Vec<u8>,
skip_enriched: bool,
) -> Result<(Option<Vec<u8>>, MessageType, EventID, Record), String> {
let (rest, (nd, ty, id)) = parse_header(&raw).map_err(|e| {
format!(
"cannot parse header: {}",
e.map_input(String::from_utf8_lossy)
)
})?;
let (rest, body) = parse_body(rest, ty, skip_enriched).map_err(|e| {
format!(
"cannot parse body: {}",
e.map_input(String::from_utf8_lossy)
)
})?;
if !rest.is_empty() {
return Err(format!(
"garbage at end of message: {}",
String::from_utf8_lossy(rest)
));
}
let nd = nd.map(|s| s.to_vec());
let mut hex_strides = Vec::with_capacity(
body.iter()
.filter(|(_, v)| matches!(v, PValue::HexStr(_)))
.count(),
);
let mut elems = Vec::with_capacity(body.len());
for (k, v) in body {
let v = match &v {
PValue::Empty => Value::Empty,
PValue::Number(n) => Value::Number(n.clone()),
PValue::Str(s, q) => Value::Str(to_range(&raw, s), *q),
PValue::List(vs) => Value::List(
vs.iter()
.map(|s| Value::Str(to_range(&raw, s), Quote::None))
.collect::<Vec<_>>(),
),
PValue::HexStr(s) => {
let o = raw.offset(s);
hex_strides.push(o..o + s.len());
Value::Str(o..o + s.len() / 2, Quote::None)
}
};
elems.push((k, v));
}
for stride in hex_strides {
for i in 0..stride.len() / 2 {
let d = unsafe {
str::from_utf8_unchecked(&raw[stride.start + 2 * i..stride.start + 2 * i + 2])
};
raw[stride.start + i] = u8::from_str_radix(d, 16).map_err(|_| {
let hex_str = unsafe { str::from_utf8_unchecked(&raw[stride.clone()]) };
format!("{} ({}) can't hex-decode {}", id, ty, hex_str)
})?;
}
}
Ok((nd, ty, id, Record { elems, raw }))
}
#[inline(always)]
fn to_range(line: &[u8], subset: &[u8]) -> Range<usize> {
let s = line.offset(subset);
s..s + subset.len()
}
#[inline(always)]
#[allow(clippy::type_complexity)]
fn parse_header(input: &[u8]) -> IResult<&[u8], (Option<&[u8]>, MessageType, EventID)> {
tuple((
opt(terminated(parse_node, is_a(" "))),
terminated(parse_type, is_a(" ")),
parse_msgid,
))(input)
}
#[inline(always)]
fn parse_node(input: &[u8]) -> IResult<&[u8], &[u8]> {
preceded(tag("node="), is_not(" \t\r\n"))(input)
}
#[inline(always)]
fn parse_type(input: &[u8]) -> IResult<&[u8], MessageType> {
preceded(
tag("type="),
alt((
map_res(
recognize(many1_count(alt((alphanumeric1, tag("_"))))),
|s| {
EVENT_IDS
.get(s)
.ok_or(format!("unknown event id {}", String::from_utf8_lossy(s)))
.map(|n| MessageType(*n))
},
),
map_res(
delimited(tag("UNKNOWN["), is_a("0123456789"), tag("]")),
|s| str::from_utf8(s).unwrap().parse::<u32>().map(MessageType),
),
)),
)(input)
}
#[inline(always)]
fn parse_msgid(input: &[u8]) -> IResult<&[u8], EventID> {
map_res(
tuple((
tag("msg=audit("),
digit1,
tag("."),
digit1,
tag(":"),
digit1,
tag("):"),
take_while(is_space),
)),
|(_, sec, _, msec, _, seq, _, _)| -> Result<EventID, std::num::ParseIntError> {
Ok(EventID {
timestamp: 1000 * unsafe { str::from_utf8_unchecked(sec) }.parse::<u64>()?
+ unsafe { str::from_utf8_unchecked(msec) }.parse::<u64>()?,
sequence: unsafe { str::from_utf8_unchecked(seq) }.parse::<u32>()?,
})
},
)(input)
}
enum PValue<'a> {
Empty,
HexStr(&'a [u8]),
Str(&'a [u8], Quote),
List(Vec<&'a [u8]>),
Number(Number),
}
#[inline(always)]
fn parse_body(
input: &[u8],
ty: MessageType,
skip_enriched: bool,
) -> IResult<&[u8], Vec<(Key, PValue)>> {
let (input, special) = opt(alt((
map_res(
tuple((
tuple((tag("avc:"), space0)),
alt((tag("granted"), tag("denied"))),
tuple((space0, tag("{"), space0)),
many1(terminated(parse_identifier, space0)),
tuple((tag("}"), space0, tag("for"), space0)),
)),
|(_, k, _, v, _)| -> Result<_, ()> { Ok((Key::Name(NVec::from(k)), PValue::List(v))) },
),
map_res(
tuple((tag("netlabel"), tag(":"), space0)),
|(s, _, _): (&[u8], _, _)| -> Result<_, ()> {
Ok((Key::Name(NVec::from(s)), PValue::Empty))
},
),
)))(input)?;
let (input, _) = match ty {
msg_type::MAC_POLICY_LOAD => opt(tag("policy loaded "))(input)?,
_ => (input, None),
};
let (input, mut kv) = if skip_enriched {
terminated(
separated_list0(tag(b" "), |input| parse_kv(input, ty)),
alt((
value((), tuple((tag("\x1d"), many1(none_of("\n")), tag("\n")))),
value((), tag("\n")),
)),
)(input)?
} else {
terminated(
separated_list0(take_while1(|c| c == b' ' || c == b'\x1d'), |input| {
parse_kv(input, ty)
}),
newline,
)(input)?
};
if let Some(s) = special {
kv.push(s)
}
Ok((input, kv))
}
#[inline(always)]
fn parse_kv(input: &[u8], ty: MessageType) -> IResult<&[u8], (Key, PValue)> {
let (input, key) = match ty {
msg_type::EXECVE if !input.is_empty() && input[0] == b'a' => terminated(
alt((parse_key_a_x_len, parse_key_a_xy, parse_key_a_x, parse_key)),
tag("="),
)(input),
msg_type::SYSCALL => terminated(alt((parse_key_a_x, parse_key)), tag("="))(input),
_ => terminated(parse_key, tag("="))(input),
}?;
let (input, value) = match (ty, &key) {
(msg_type::SYSCALL, Key::Arg(_, None)) => map_res(
recognize(terminated(
many1_count(take_while1(is_hex_digit)),
peek(take_while1(is_sep)),
)),
|s| -> Result<_, ()> {
let ps = unsafe { str::from_utf8_unchecked(s) };
match u64::from_str_radix(ps, 16) {
Ok(n) => Ok(PValue::Number(Number::Hex(n))),
Err(_) => Ok(PValue::Str(s, Quote::None)),
}
},
)(input)?,
(msg_type::SYSCALL, Key::Common(c)) => parse_common(input, ty, *c)?,
(msg_type::EXECVE, Key::Arg(_, _)) => parse_encoded(input)?,
(msg_type::EXECVE, Key::ArgLen(_)) => parse_dec(input)?,
(_, Key::Name(name)) => parse_named(input, ty, name)?,
(_, Key::Common(c)) => parse_common(input, ty, *c)?,
(_, Key::NameUID(name)) | (_, Key::NameGID(name)) => {
alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input)?
}
_ => parse_encoded(input)?,
};
Ok((input, (key, value)))
}
#[inline(always)]
fn parse_named<'a>(input: &'a [u8], ty: MessageType, name: &[u8]) -> IResult<&'a [u8], PValue<'a>> {
match FIELD_TYPES.get(name) {
Some(&FieldType::Encoded) => {
alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input)
}
Some(&FieldType::NumericHex) => {
alt((parse_hex, |input| parse_unspec_value(input, ty, name)))(input)
}
Some(&FieldType::NumericDec) => {
alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input)
}
Some(&FieldType::NumericOct) => {
alt((parse_oct, |input| parse_unspec_value(input, ty, name)))(input)
}
_ => alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input),
}
}
#[inline(always)]
fn parse_common(input: &[u8], ty: MessageType, c: Common) -> IResult<&[u8], PValue> {
let name = <&str>::from(c).as_bytes();
match c {
Common::Arch => alt((parse_hex, |input| parse_unspec_value(input, ty, name)))(input),
Common::Syscall
| Common::Items
| Common::Pid
| Common::PPid
| Common::Exit
| Common::Ses => alt((parse_dec, |input| parse_unspec_value(input, ty, name)))(input),
Common::Success | Common::Tty | Common::Comm | Common::Exe | Common::Subj | Common::Key => {
alt((parse_encoded, |input| parse_unspec_value(input, ty, name)))(input)
}
}
}
#[inline(always)]
fn parse_encoded(input: &[u8]) -> IResult<&[u8], PValue> {
alt((
map_res(parse_str_dq_safe, |s| -> Result<_, ()> {
Ok(PValue::Str(s, Quote::Double))
}),
map_res(
terminated(
recognize(many1_count(take_while_m_n(2, 2, is_hex_digit))),
peek(take_while1(is_sep)),
),
|s| -> Result<_, ()> { Ok(PValue::HexStr(s)) },
),
map_res(
terminated(alt((tag("(null)"), tag("?"))), peek(take_while1(is_sep))),
|_| -> Result<_, ()> { Ok(PValue::Empty) },
),
))(input)
}
#[inline(always)]
fn parse_hex(input: &[u8]) -> IResult<&[u8], PValue> {
map_res(
terminated(take_while1(is_hex_digit), peek(take_while1(is_sep))),
|digits| -> Result<_, std::num::ParseIntError> {
let digits = unsafe { str::from_utf8_unchecked(digits) };
Ok(PValue::Number(Number::Hex(u64::from_str_radix(
digits, 16,
)?)))
},
)(input)
}
#[inline(always)]
fn parse_dec(input: &[u8]) -> IResult<&[u8], PValue> {
map_res(
terminated(
pair(opt(tag("-")), take_while1(is_digit)),
peek(take_while1(is_sep)),
),
|(sign, digits)| -> Result<_, std::num::ParseIntError> {
let sign = if sign.is_some() { -1 } else { 1 };
let digits = unsafe { str::from_utf8_unchecked(digits) };
Ok(PValue::Number(Number::Dec(sign * i64::from_str(digits)?)))
},
)(input)
}
#[inline(always)]
fn parse_oct(input: &[u8]) -> IResult<&[u8], PValue> {
map_res(
terminated(take_while1(is_oct_digit), peek(take_while1(is_sep))),
|digits| -> Result<_, std::num::ParseIntError> {
let digits = unsafe { str::from_utf8_unchecked(digits) };
Ok(PValue::Number(Number::Oct(u64::from_str_radix(digits, 8)?)))
},
)(input)
}
#[inline(always)]
fn parse_unspec_value<'a>(
input: &'a [u8],
ty: MessageType,
name: &[u8],
) -> IResult<&'a [u8], PValue<'a>> {
match (ty, name) {
(msg_type::SYSCALL, b"subj") | (msg_type::USER_AUTH, b"subj") => {
if let Ok((input, s)) = recognize(tuple((
opt(tag("=")),
parse_str_unq,
opt(delimited(tag(" ("), parse_identifier, tag(")"))),
)))(input)
{
return Ok((input, PValue::Str(s, Quote::None)));
}
}
(msg_type::AVC, b"info") => {
if let Ok((input, s)) = parse_str_dq(input) {
return Ok((input, PValue::Str(s, Quote::None)));
}
}
_ => (),
};
alt((
map_res(
terminated(take_while1(is_safe_unquoted_chr), peek(take_while1(is_sep))),
|s| -> Result<_, ()> { Ok(PValue::Str(s, Quote::None)) },
),
map_res(parse_kv_sq, |s| -> Result<_, ()> {
Ok(PValue::Str(s, Quote::Single))
}),
map_res(parse_str_sq, |s| -> Result<_, ()> {
Ok(PValue::Str(s, Quote::Single))
}),
map_res(parse_str_dq, |s| -> Result<_, ()> {
Ok(PValue::Str(s, Quote::Double))
}),
map_res(parse_kv_braced, |s| -> Result<_, ()> {
Ok(PValue::Str(s, Quote::Braces))
}),
map_res(parse_str_braced, |s| -> Result<_, ()> {
Ok(PValue::Str(s, Quote::Braces))
}),
map_res(peek(take_while1(is_sep)), |_| -> Result<_, ()> {
Ok(PValue::Empty)
}),
))(input)
}
#[inline(always)]
fn parse_str_sq(input: &[u8]) -> IResult<&[u8], &[u8]> {
delimited(tag("'"), take_while(|c| c != b'\''), tag("'"))(input)
}
#[inline(always)]
fn parse_str_dq_safe(input: &[u8]) -> IResult<&[u8], &[u8]> {
delimited(tag("\""), take_while(is_safe_chr), tag("\""))(input)
}
#[inline(always)]
fn parse_str_dq(input: &[u8]) -> IResult<&[u8], &[u8]> {
delimited(tag("\""), take_while(|c| c != b'"'), tag("\""))(input)
}
#[inline(always)]
fn parse_str_braced(input: &[u8]) -> IResult<&[u8], &[u8]> {
delimited(tag("{"), take_while(|c| c != b'}'), tag("}"))(input)
}
#[inline(always)]
fn parse_str_unq(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(is_safe_chr)(input)
}
#[inline(always)]
fn parse_str_unq_inside_sq(input: &[u8]) -> IResult<&[u8], &[u8]> {
take_while(|c| is_safe_chr(c) && c != b'\'')(input)
}
#[inline(always)]
fn parse_kv_sq(input: &[u8]) -> IResult<&[u8], &[u8]> {
delimited(
tag("'"),
recognize(separated_list0(
tag(" "),
tuple((
recognize(pair(alpha1, many0_count(alt((alphanumeric1, is_a("-_")))))),
tag("="),
alt((parse_str_dq, parse_str_braced, parse_str_unq_inside_sq)),
)),
)),
tag("'"),
)(input)
}
#[inline(always)]
fn parse_kv_braced(input: &[u8]) -> IResult<&[u8], &[u8]> {
delimited(
tag("{ "),
recognize(separated_list0(
tag(" "),
tuple((
recognize(pair(alpha1, many0_count(alt((alphanumeric1, is_a("-_")))))),
tag("="),
alt((parse_str_sq, parse_str_dq, parse_str_unq)),
)),
)),
tag(" }"),
)(input)
}
#[inline(always)]
fn parse_key(input: &[u8]) -> IResult<&[u8], Key> {
map_res(
recognize(pair(alpha1, many0_count(alt((alphanumeric1, is_a("-_")))))),
|s: &[u8]| -> Result<_, ()> {
if let Ok(c) = Common::try_from(s) {
Ok(Key::Common(c))
} else if s.ends_with(b"uid") {
Ok(Key::NameUID(NVec::from(s)))
} else if s.ends_with(b"gid") {
Ok(Key::NameGID(NVec::from(s)))
} else {
Ok(Key::Name(NVec::from(s)))
}
},
)(input)
}
#[inline(always)]
fn parse_key_a_x_len(input: &[u8]) -> IResult<&[u8], Key> {
map_res(
delimited(tag("a"), digit1, tag("_len")),
|x| -> Result<_, std::num::ParseIntError> {
let x = unsafe { str::from_utf8_unchecked(x) }.parse()?;
Ok(Key::ArgLen(x))
},
)(input)
}
#[inline(always)]
fn parse_key_a_xy(input: &[u8]) -> IResult<&[u8], Key> {
map_res(
tuple((tag("a"), digit1, tag("["), digit1, tag("]"))),
|(_, x, _, y, _)| -> Result<Key, std::num::ParseIntError> {
let x = unsafe { str::from_utf8_unchecked(x) }.parse()?;
let y = unsafe { str::from_utf8_unchecked(y) }.parse()?;
Ok(Key::Arg(x, Some(y)))
},
)(input)
}
#[inline(always)]
fn parse_key_a_x(input: &[u8]) -> IResult<&[u8], Key> {
map_res(
preceded(tag("a"), digit1),
|x| -> Result<Key, std::num::ParseIntError> {
let x = unsafe { str::from_utf8_unchecked(x) }.parse()?;
Ok(Key::Arg(x, None))
},
)(input)
}
#[inline(always)]
fn parse_identifier(input: &[u8]) -> IResult<&[u8], &[u8]> {
recognize(pair(
alt((alpha1, tag("_"))),
many0_count(alt((alphanumeric1, tag("_")))),
))(input)
}
#[inline(always)]
fn is_safe_chr(c: u8) -> bool {
c == b'!' || (b'#'..=b'~').contains(&c)
}
#[inline(always)]
fn is_safe_unquoted_chr(c: u8) -> bool {
(b'#'..=b'&').contains(&c) || (b'('..=b'z').contains(&c) || c == b'!' || c == b'|' || c == b'~'
}
#[inline(always)]
fn is_sep(c: u8) -> bool {
c == b' ' || c == b'\x1d' || c == b'\n'
}
#[cfg(test)]
mod test {
use super::msg_type::*;
use super::*;
fn do_parse<T>(text: T) -> Result<(Option<Vec<u8>>, MessageType, EventID, Record), String>
where
T: AsRef<[u8]>,
{
parse(Vec::from(text.as_ref()), false)
}
#[test]
fn parser() {
assert_eq!(format!("--{}--", EOE), "--EOE--");
assert_eq!(format!("--{}--", MessageType(9999)), "--UNKNOWN[9999]--");
let (_, t, id, _rv) = do_parse(include_bytes!("testdata/line-eoe.txt")).unwrap();
assert_eq!(t, EOE);
assert_eq!(
id,
EventID {
timestamp: 1615225617302,
sequence: 25836
}
);
let (_, t, id, rv) = do_parse(include_bytes!("testdata/line-syscall.txt")).unwrap();
assert_eq!(t, SYSCALL);
assert_eq!(
id,
EventID {
timestamp: 1615114232375,
sequence: 15558
}
);
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"arch: Num:<0xc000003e>",
"syscall: Num:<59>",
"success: Str:<yes>",
"exit: Num:<0>",
"a0: Num:<0x63b29337fd18>",
"a1: Num:<0x63b293387d58>",
"a2: Num:<0x63b293375640>",
"a3: Num:<0xfffffffffffff000>",
"items: Num:<2>",
"ppid: Num:<10883>",
"pid: Num:<10884>",
"auid: Num:<1000>",
"uid: Num:<0>",
"gid: Num:<0>",
"euid: Num:<0>",
"suid: Num:<0>",
"fsuid: Num:<0>",
"egid: Num:<0>",
"sgid: Num:<0>",
"fsgid: Num:<0>",
"tty: Str:<pts1>",
"ses: Num:<1>",
"comm: Str:<whoami>",
"exe: Str:</usr/bin/whoami>",
"key: Empty",
"ARCH: Str:<x86_64>",
"SYSCALL: Str:<execve>",
"AUID: Str:<user>",
"UID: Str:<root>",
"GID: Str:<root>",
"EUID: Str:<root>",
"SUID: Str:<root>",
"FSUID: Str:<root>",
"EGID: Str:<root>",
"SGID: Str:<root>",
"FSGID: Str:<root>",
)
);
let (_, t, id, rv) = do_parse(include_bytes!("testdata/line-execve.txt")).unwrap();
assert_eq!(t, EXECVE);
assert_eq!(
id,
EventID {
timestamp: 1614788539386,
sequence: 13232
}
);
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!("argc: Num:<0>", "a0: Str:<whoami>")
);
let (_, t, id, rv) = do_parse(include_bytes!("testdata/line-path.txt")).unwrap();
assert_eq!(t, PATH);
assert_eq!(
id,
EventID {
timestamp: 1614788539386,
sequence: 13232
}
);
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"item: Num:<0>",
"name: Str:</usr/bin/whoami>",
"inode: Num:<261214>",
"dev: Str:<ca:03>",
"mode: Num:<0o100755>",
"ouid: Num:<0>",
"ogid: Num:<0>",
"rdev: Str:<00:00>",
"nametype: Str:<NORMAL>",
"cap_fp: Num:<0x0>",
"cap_fi: Num:<0x0>",
"cap_fe: Num:<0>",
"cap_fver: Num:<0x0>",
)
);
let (_, t, id, rv) = do_parse(include_bytes!("testdata/line-path-enriched.txt")).unwrap();
assert_eq!(t, PATH);
assert_eq!(
id,
EventID {
timestamp: 1615113648978,
sequence: 15219
}
);
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"item: Num:<1>",
"name: Str:</lib64/ld-linux-x86-64.so.2>",
"inode: Num:<262146>",
"dev: Str:<ca:03>",
"mode: Num:<0o100755>",
"ouid: Num:<0>",
"ogid: Num:<0>",
"rdev: Str:<00:00>",
"nametype: Str:<NORMAL>",
"cap_fp: Num:<0x0>",
"cap_fi: Num:<0x0>",
"cap_fe: Num:<0>",
"cap_fver: Num:<0x0>",
"OUID: Str:<root>",
"OGID: Str:<root>",
)
);
let (_, t, id, rv) = do_parse(include_bytes!("testdata/line-user-acct.txt")).unwrap();
assert_eq!(t, USER_ACCT);
assert_eq!(
id,
EventID {
timestamp: 1615113648981,
sequence: 15220
}
);
assert_eq!(rv.into_iter().map(|(k,v)| format!("{:?}: {:?}", k, v)).collect::<Vec<_>>(),
vec!("pid: Num:<9460>",
"uid: Num:<1000>",
"auid: Num:<1000>",
"ses: Num:<1>",
"msg: Str:<op=PAM:accounting grantors=pam_permit acct=\"user\" exe=\"/usr/bin/sudo\" hostname=? addr=? terminal=/dev/pts/1 res=success>",
"UID: Str:<user>",
"AUID: Str:<user>",
));
let (_, t, id, _) = do_parse(include_bytes!("testdata/line-unknown.txt")).unwrap();
assert_eq!(t, BPF);
assert_eq!(
id,
EventID {
timestamp: 1626883065201,
sequence: 216697
}
);
let (_, t, _, rv) = do_parse(include_bytes!("testdata/line-avc-denied.txt")).unwrap();
assert_eq!(t, AVC);
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"pid: Num:<15381>",
"comm: Str:<laurel>",
"capability: Num:<7>",
"scontext: Str:<system_u:system_r:auditd_t:s0>",
"tcontext: Str:<system_u:system_r:auditd_t:s0>",
"tclass: Str:<capability>",
"permissive: Num:<1>",
"denied: List:<setuid>",
)
);
let (_, t, _, rv) = do_parse(include_bytes!("testdata/line-avc-granted.txt")).unwrap();
assert_eq!(t, AVC);
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"pid: Num:<11209>",
"comm: Str:<tuned>",
"scontext: Str:<system_u:system_r:tuned_t:s0>",
"tcontext: Str:<system_u:object_r:security_t:s0>",
"tclass: Str:<security>",
"granted: List:<setsecparam>",
)
);
let (_, t, _, rv) = do_parse(include_bytes!("testdata/line-netlabel.txt")).unwrap();
assert_eq!(t, MAC_UNLBL_ALLOW);
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"auid: Num:<0>",
"ses: Num:<0>",
"unlbl_accept: Str:<1>",
"old: Str:<0>",
"AUID: Str:<root>",
"netlabel: Empty",
)
);
let (_, _, _, rv) = do_parse(include_bytes!("testdata/line-broken-subj1.txt")).unwrap();
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"arch: Num:<0xc000003e>",
"syscall: Num:<59>",
"success: Str:<yes>",
"exit: Num:<0>",
"a0: Num:<0x55b26d44a6a0>",
"a1: Num:<0x55b26d44a878>",
"a2: Num:<0x55b26d44a8e8>",
"a3: Num:<0x7faeccab5850>",
"items: Num:<2>",
"ppid: Num:<659>",
"pid: Num:<661>",
"auid: Num:<4294967295>",
"uid: Num:<0>",
"gid: Num:<0>",
"euid: Num:<0>",
"suid: Num:<0>",
"fsuid: Num:<0>",
"egid: Num:<0>",
"sgid: Num:<0>",
"fsgid: Num:<0>",
"tty: Str:<(none)>",
"ses: Num:<4294967295>",
"comm: Str:<dhclient>",
"exe: Str:</sbin/dhclient>",
"subj: Str:</{,usr/}sbin/dhclient>",
"key: Empty",
)
);
let (_, _, _, rv) = do_parse(include_bytes!("testdata/line-broken-subj2.txt")).unwrap();
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"arch: Num:<0xc000003e>",
"syscall: Num:<49>",
"success: Str:<yes>",
"exit: Num:<0>",
"a0: Num:<0x15>",
"a1: Num:<0x55c5e046e264>",
"a2: Num:<0x1c>",
"a3: Num:<0x7ffc8fab77ec>",
"items: Num:<0>",
"ppid: Num:<1899774>",
"pid: Num:<1899780>",
"auid: Num:<4294967295>",
"uid: Num:<0>",
"gid: Num:<0>",
"euid: Num:<0>",
"suid: Num:<0>",
"fsuid: Num:<0>",
"egid: Num:<0>",
"sgid: Num:<0>",
"fsgid: Num:<0>",
"tty: Str:<(none)>",
"ses: Num:<4294967295>",
"comm: Str:<ntpd>",
"exe: Str:</usr/sbin/ntpd>",
"subj: Str:<=/usr/sbin/ntpd (enforce)>",
"key: Empty",
)
);
let (_, _, _, rv) = do_parse(include_bytes!("testdata/line-broken-avc-info.txt")).unwrap();
assert_eq!(
rv.into_iter()
.map(|(k, v)| format!("{:?}: {:?}", k, v))
.collect::<Vec<_>>(),
vec!(
"apparmor: Str:<STATUS>",
"operation: Str:<profile_replace>",
"info: Str:<same as current profile, skipping>",
"profile: Str:<unconfined>",
"name: Str:<snap-update-ns.amazon-ssm-agent>",
"pid: Num:<3981295>",
"comm: Str:<apparmor_parser>",
)
);
do_parse(include_bytes!("testdata/line-daemon-end.txt")).unwrap();
do_parse(include_bytes!("testdata/line-netfilter.txt")).unwrap();
do_parse(include_bytes!("testdata/line-anom-abend.txt")).unwrap();
do_parse(include_bytes!("testdata/line-user-auth.txt")).unwrap();
do_parse(include_bytes!("testdata/line-sockaddr-unix.txt")).unwrap();
do_parse(include_bytes!("testdata/line-user-auth-2.txt")).unwrap();
}
#[test]
#[should_panic]
fn breakage_sockaddr_unknown() {
do_parse(include_bytes!("testdata/line-sockaddr-unknown.txt")).unwrap();
}
#[test]
fn breakage_mac_policy_load() {
do_parse(include_bytes!("testdata/line-mac-policy-load.txt")).unwrap();
}
}