use crate::*;
#[cfg(feature = "serde")]
use serde_test::{assert_de_tokens, assert_ser_tokens, Token};
#[test]
fn parser() {
assert_eq!(format!("--{}--", MessageType::EOE), "--EOE--");
assert_eq!(format!("--{}--", MessageType(9999)), "--UNKNOWN[9999]--");
let msg = parse(include_bytes!("testdata/line-eoe.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::EOE);
assert_eq!(
msg.id,
EventID {
timestamp: 1615225617302,
sequence: 25836
}
);
let msg = parse(include_bytes!("testdata/line-syscall.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::SYSCALL);
assert_eq!(
msg.id,
EventID {
timestamp: 1615114232375,
sequence: 15558
}
);
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-execve.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::EXECVE);
assert_eq!(
msg.id,
EventID {
timestamp: 1614788539386,
sequence: 13232
}
);
assert_eq!(
msg.body
.into_iter()
.map(|(k, v)| format!("{k:?}: {v:?}"))
.collect::<Vec<_>>(),
vec!("argc: Num:<0>", "a0: Str:<whoami>")
);
let msg = parse(include_bytes!("testdata/line-path.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::PATH);
assert_eq!(
msg.id,
EventID {
timestamp: 1614788539386,
sequence: 13232
}
);
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-path-enriched.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::PATH);
assert_eq!(
msg.id,
EventID {
timestamp: 1615113648978,
sequence: 15219
}
);
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-user-acct.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::USER_ACCT);
assert_eq!(
msg.id,
EventID {
timestamp: 1615113648981,
sequence: 15220
}
);
assert_eq!(
msg.body
.into_iter()
.map(|(k, v)| format!("{k:?}: {v:?}"))
.collect::<Vec<_>>(),
vec!(
"pid: Num:<9460>",
"uid: Num:<1000>",
"auid: Num:<1000>",
"ses: Num:<1>",
"msg: Map:<op=Str:<PAM:accounting> grantors=Str:<pam_permit> acct=Str:<user> exe=Str:</usr/bin/sudo> hostname=Empty addr=Empty terminal=Str:</dev/pts/1> res=Str:<success>>",
"UID: Str:<user>",
"AUID: Str:<user>",
)
);
let msg = Parser {
enriched: false,
split_msg: false,
}
.parse(include_bytes!("testdata/line-user-acct.txt"))
.unwrap();
assert_eq!(
msg.body
.into_iter()
.map(|(k, v)| format!("{k:?}: {v:?}"))
.collect::<Vec<_>>(),
vec!(
"pid: Num:<9460>",
"uid: Num:<1000>",
"auid: Num:<1000>",
"ses: Num:<1>",
r#"msg: Str:<op=PAM:accounting grantors=pam_permit acct="user" exe="/usr/bin/sudo" hostname=? addr=? terminal=/dev/pts/1 res=success>"#,
)
);
let msg = parse(include_bytes!("testdata/line-unknown.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::BPF);
assert_eq!(
msg.id,
EventID {
timestamp: 1626883065201,
sequence: 216697
}
);
let msg = parse(include_bytes!("testdata/line-avc-denied.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::AVC);
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-avc-granted.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::AVC);
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-netlabel.txt"), false).unwrap();
assert_eq!(msg.ty, MessageType::MAC_UNLBL_ALLOW);
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-broken-subj1.txt"), false).unwrap();
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-broken-subj2.txt"), false).unwrap();
assert_eq!(
msg.body
.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 msg = parse(include_bytes!("testdata/line-broken-avc-info.txt"), false).unwrap();
assert_eq!(
msg.body
.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>",
)
);
for enriched in [true, false] {
for split_msg in [true, false] {
for (n, line) in [
&include_bytes!("testdata/line-acct-lock.txt")[..],
&include_bytes!("testdata/line-add-group.txt")[..],
&include_bytes!("testdata/line-add-user.txt")[..],
&include_bytes!("testdata/line-anom-abend-2.txt")[..],
&include_bytes!("testdata/line-anom-abend.txt")[..],
&include_bytes!("testdata/line-avc-denied.txt")[..],
&include_bytes!("testdata/line-avc-granted.txt")[..],
&include_bytes!("testdata/line-bpf.txt")[..],
&include_bytes!("testdata/line-broken-avc-info.txt")[..],
&include_bytes!("testdata/line-broken-subj1.txt")[..],
&include_bytes!("testdata/line-broken-subj2.txt")[..],
&include_bytes!("testdata/line-chgrp-id.txt")[..],
&include_bytes!("testdata/line-cred-acq.txt")[..],
&include_bytes!("testdata/line-cred-disp.txt")[..],
&include_bytes!("testdata/line-cred-refr.txt")[..],
&include_bytes!("testdata/line-crypto-key-user.txt")[..],
&include_bytes!("testdata/line-crypto-param-change-user.txt")[..],
&include_bytes!("testdata/line-crypto-session.txt")[..],
&include_bytes!("testdata/line-daemon-end-2.txt")[..],
&include_bytes!("testdata/line-daemon-end.txt")[..],
&include_bytes!("testdata/line-daemon-start.txt")[..],
&include_bytes!("testdata/line-del-group.txt")[..],
&include_bytes!("testdata/line-del-user.txt")[..],
&include_bytes!("testdata/line-eoe.txt")[..],
&include_bytes!("testdata/line-execve.txt")[..],
&include_bytes!("testdata/line-grp-mgmt.txt")[..],
&include_bytes!("testdata/line-mac-policy-load.txt")[..],
&include_bytes!("testdata/line-netfilter.txt")[..],
&include_bytes!("testdata/line-netlabel.txt")[..],
&include_bytes!("testdata/line-path-enriched.txt")[..],
&include_bytes!("testdata/line-path.txt")[..],
&include_bytes!("testdata/line-sockaddr-unix-2.txt")[..],
&include_bytes!("testdata/line-sockaddr-unix.txt")[..],
&include_bytes!("testdata/line-sockaddr-unknown-1.txt")[..],
&include_bytes!("testdata/line-sockaddr-unknown-2.txt")[..],
&include_bytes!("testdata/line-sockaddr-unknown-3.txt")[..],
&include_bytes!("testdata/line-software-update.txt")[..],
&include_bytes!("testdata/line-syscall.txt")[..],
&include_bytes!("testdata/line-tty.txt")[..],
&include_bytes!("testdata/line-unknown.txt")[..],
&include_bytes!("testdata/line-uringop.txt")[..],
&include_bytes!("testdata/line-user-acct.txt")[..],
&include_bytes!("testdata/line-user-auth-2.txt")[..],
&include_bytes!("testdata/line-user-auth.txt")[..],
&include_bytes!("testdata/line-user-avc-1.txt")[..],
&include_bytes!("testdata/line-user-avc-2.txt")[..],
&include_bytes!("testdata/line-user-chauthtok.txt")[..],
&include_bytes!("testdata/line-user-end.txt")[..],
&include_bytes!("testdata/line-user-err.txt")[..],
&include_bytes!("testdata/line-user-login.txt")[..],
&include_bytes!("testdata/line-user-logout.txt")[..],
&include_bytes!("testdata/line-user-mgmt.txt")[..],
&include_bytes!("testdata/line-user-role-change.txt")[..],
&include_bytes!("testdata/line-user-selinux-err.txt")[..],
&include_bytes!("testdata/line-user-start.txt")[..],
&include_bytes!("testdata/line-usys-config.txt")[..],
]
.iter()
.enumerate()
{
Parser {
enriched,
split_msg,
}
.parse(line)
.unwrap_or_else(|_| {
panic!("failed to parse {n} (enriched={enriched}, split_msg={split_msg}")
});
}
}
}
}
#[test]
fn test_msg_kv() {
let p = Parser {
split_msg: true,
..Parser::default()
};
for (n, line) in [
&include_bytes!("testdata/line-acct-lock.txt")[..],
&include_bytes!("testdata/line-add-group.txt")[..],
&include_bytes!("testdata/line-add-user.txt")[..],
&include_bytes!("testdata/line-chgrp-id.txt")[..],
&include_bytes!("testdata/line-cred-acq.txt")[..],
&include_bytes!("testdata/line-cred-disp.txt")[..],
&include_bytes!("testdata/line-cred-refr.txt")[..],
&include_bytes!("testdata/line-crypto-key-user.txt")[..],
&include_bytes!("testdata/line-crypto-session.txt")[..],
&include_bytes!("testdata/line-crypto-param-change-user.txt")[..],
&include_bytes!("testdata/line-daemon-end-2.txt")[..],
&include_bytes!("testdata/line-del-group.txt")[..],
&include_bytes!("testdata/line-del-user.txt")[..],
&include_bytes!("testdata/line-grp-mgmt.txt")[..],
&include_bytes!("testdata/line-software-update.txt")[..],
&include_bytes!("testdata/line-user-acct.txt")[..],
&include_bytes!("testdata/line-user-auth.txt")[..],
&include_bytes!("testdata/line-user-auth-2.txt")[..],
&include_bytes!("testdata/line-user-chauthtok.txt")[..],
&include_bytes!("testdata/line-user-end.txt")[..],
&include_bytes!("testdata/line-user-err.txt")[..],
&include_bytes!("testdata/line-user-login.txt")[..],
&include_bytes!("testdata/line-user-logout.txt")[..],
&include_bytes!("testdata/line-user-mgmt.txt")[..],
&include_bytes!("testdata/line-user-role-change.txt")[..],
&include_bytes!("testdata/line-user-start.txt")[..],
&include_bytes!("testdata/line-usys-config.txt")[..],
&include_bytes!("testdata/line-user-avc-1.txt")[..],
&include_bytes!("testdata/line-user-avc-2.txt")[..],
&include_bytes!("testdata/line-user-selinux-err.txt")[..],
]
.iter()
.enumerate()
{
let Message {
node: _,
ty: _,
id,
body,
} = p.parse(line).unwrap();
println!("test {n}: {id}: {body:?}");
let msg = body
.get("msg")
.unwrap_or_else(|| panic!("test {n}: {id}: Field msg not found"));
match msg {
Value::Map(_) => {}
Value::Str(_, _) => panic!("test {n}: {id}: Field msg was parsed as string"),
_ => panic!("test {n}: {id}: Field msg was parsed as something else"),
}
}
}
#[test]
fn breakage_sockaddr_unknown() {
parse(
include_bytes!("testdata/line-sockaddr-unknown-1.txt"),
false,
)
.expect("can't parse line-sockaddr-unknown-1.txt");
parse(
include_bytes!("testdata/line-sockaddr-unknown-2.txt"),
false,
)
.expect("can't parse line-sockaddr-unknown-2.txt");
parse(
include_bytes!("testdata/line-sockaddr-unknown-3.txt"),
false,
)
.expect("can't parse line-sockaddr-unknown-3.txt");
}
#[test]
#[cfg(feature = "serde")]
fn serde_messagetype() {
assert_ser_tokens(&MessageType::SYSCALL, &[Token::String("SYSCALL")]);
assert_ser_tokens(&MessageType(20000), &[Token::String("UNKNOWN[20000]")]);
}
#[test]
#[cfg(feature = "serde")]
fn serde_key() {
for (obj, tok) in &[
(&Key::Name(b"foo"[..].into()), &[Token::String("foo")]),
(&Key::NameUID(b"euid"[..].into()), &[Token::String("euid")]),
(&Key::NameGID(b"egid"[..].into()), &[Token::String("egid")]),
(&Key::Common(Common::Arch), &[Token::String("arch")]),
(&Key::Arg(1, None), &[Token::String("a1")]),
(&Key::Arg(2, Some(3)), &[Token::String("a2[3]")]),
(&Key::ArgLen(2), &[Token::String("a2_len")]),
] {
assert_ser_tokens(obj, *tok);
assert_de_tokens(*obj, *tok);
}
for (obj, tok) in &[
(
&Key::NameTranslated(b"foo"[..].into()),
&[Token::String("FOO")],
),
(&Key::Literal("foo"), &[Token::String("foo")]),
] {
assert_ser_tokens(obj, *tok);
}
}
#[test]
#[cfg(feature = "serde")]
fn serde_value() {
for q in &[Quote::None, Quote::Single, Quote::Double] {
assert_ser_tokens(&Value::Str(&b"foo"[..], *q), &[Token::Bytes(b"foo")]);
}
assert_ser_tokens(
&Value::Str(&b"foo"[..], Quote::Braces),
&[Token::Bytes(b"{foo}")],
);
for (obj, tok) in &[
(Value::Empty, &[Token::Unit][..]),
(Value::Owned(b"foo".to_vec()), &[Token::Bytes(b"foo")]),
(Value::Number(Number::Hex(16)), &[Token::String("0x10")]),
(Value::Number(Number::Oct(16)), &[Token::String("0o20")]),
(Value::Number(Number::Dec(16)), &[Token::I64(16)]),
(
Value::List(vec![]),
&[Token::Seq { len: Some(0) }, Token::SeqEnd][..],
),
(
Value::List(vec![
Value::Owned(b"foo".to_vec()),
Value::Owned(b"bar".to_vec()),
Value::Owned(b"baz".to_vec()),
Value::from(42),
]),
&[
Token::Seq { len: Some(4) },
Token::Bytes(b"foo"),
Token::Bytes(b"bar"),
Token::Bytes(b"baz"),
Token::I64(42),
Token::SeqEnd,
][..],
),
(
Value::Map(vec![]),
&[Token::Map { len: Some(0) }, Token::MapEnd][..],
),
(
Value::Map(vec![(
Key::Name(b"foo"[..].into()),
Value::Owned(b"bar".to_vec()),
)]),
&[
Token::Map { len: Some(1) },
Token::String("foo"),
Token::Bytes(b"bar"),
Token::MapEnd,
][..],
),
] {
assert_ser_tokens(obj, &tok[..]);
assert_de_tokens(obj, &tok[..]);
}
}
#[test]
#[cfg(feature = "serde")]
fn serde_number() {
for (n, t) in &[
(Number::Dec(10), Token::I64(10)),
(Number::Hex(0x10), Token::String("0x10")),
(Number::Oct(0o10), Token::String("0o10")),
] {
assert_ser_tokens(n, &[*t]);
assert_de_tokens(n, &[*t]);
}
assert_de_tokens(&Number::Dec(10), &[Token::U64(10)]);
}
#[test]
#[cfg(feature = "serde")]
fn serde_event_id() {
let obj = EventID {
timestamp: 1615225617302,
sequence: 25836,
};
let tok = Token::String("1615225617.302:25836");
assert_ser_tokens(&obj, &[tok]);
assert_de_tokens(&obj, &[tok]);
}
#[test]
#[cfg(feature = "serde")]
fn serde_message_type() {
for (m, t) in &[
(MessageType::SYSCALL, Token::String("SYSCALL")),
(MessageType(9999), Token::String("UNKNOWN[9999]")),
] {
assert_ser_tokens(m, &[*t]);
assert_de_tokens(m, &[*t]);
}
}
#[test]
#[cfg(feature = "serde")]
fn serde_message() {
let msg = parse(include_bytes!("testdata/line-eoe.txt"), false).unwrap();
assert_ser_tokens(&msg.body, &[Token::Map { len: Some(0) }, Token::MapEnd]);
let msg = parse(include_bytes!("testdata/line-execve.txt"), false).unwrap();
assert_ser_tokens(
&msg.body,
&[
Token::Map { len: Some(2) },
Token::String("argc"),
Token::I64(0),
Token::String("a0"),
Token::Bytes(b"whoami"),
Token::MapEnd,
],
);
}
#[test]
fn parse_uringop() {
let msg = parse(include_bytes!("testdata/line-uringop.txt"), false).unwrap();
println!("{msg:?}");
let v = msg
.body
.get("uring_op")
.unwrap_or_else(|| panic!("{}: uring_op not found", msg.id));
assert_eq!(*v, Value::Number(Number::Dec(18)));
}
#[test]
fn parse_bpf() {
let msg = parse(include_bytes!("testdata/line-bpf.txt"), false).unwrap();
println!("{msg:?}");
let v = msg
.body
.get("prog-id")
.unwrap_or_else(|| panic!("{}: prog-id not found", msg.id));
assert_eq!(*v, Value::Number(Number::Dec(75)));
}
#[test]
fn special() {
Parser {
enriched: false,
split_msg: false,
}
.parse(&include_bytes!("testdata/line-daemon-start.txt")[..])
.unwrap_or_else(|e| panic!("{e}"));
}