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

Achiefs / fim / 19957096193

05 Dec 2025 08:23AM UTC coverage: 83.04% (-3.3%) from 86.318%
19957096193

push

github

okynos
Fix unit tests of Audit event

1180 of 1421 relevant lines covered (83.04%)

1.58 hits per line

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

81.58
/src/logreader.rs
1
// Copyright (C) 2022, Achiefs.
2

3
// Global constants definitions
4
pub const AUDIT_PATH: &str = "/var/log/audit";
5
pub const AUDIT_LOG_PATH: &str = "/var/log/audit/audit.log";
6

7
// To manage file reading
8
use std::io::{BufReader, SeekFrom};
9
use std::io::prelude::*;
10
// To manage readed data into collection
11
use std::collections::HashMap;
12
// To log the program process
13
use log::{debug, error};
14

15
// Single event data management
16
use crate::events::Event;
17
use crate::events::AuditEvent;
18
// To manage common functions
19
use crate::utils;
20
// To get configuration constants
21
use crate::appconfig::*;
22

23
// Defined type to simplify syntax
24
type SHashMap = HashMap<String, String>;
25

26
// ----------------------------------------------------------------------------
27

28
// Read file to extract last data until the Audit ID changes
29
pub fn read_log(file: String, cfg: AppConfig, position: u64, itx: u64) -> (Event, u64) {
1✔
30
    let mut event: Event = Event::Audit(Box::new(AuditEvent::new()));
2✔
31
    let mut current_position = position;
1✔
32
    let log = utils::open_file(&file, 0);
2✔
33
    let mut buff = BufReader::new(log);
1✔
34
    match buff.seek(SeekFrom::Current(position as i64)) {
2✔
35
        Ok(p) => debug!("Seek audit log file, position: {}", p),
2✔
36
        Err(e) => error!("{}", e)
×
37
    };
38

39
    // Read from last registered position until we get event or the end
40
    let mut data: Vec<HashMap<String, String>> = Vec::new();
1✔
41
    let mut line = String::new();
2✔
42
    while current_position < utils::get_file_end(&file, 0) {
2✔
43
        debug!("Reading start: {}", current_position);
2✔
44
        let bytes_read = match buff.read_line(&mut line){
2✔
45
            Ok(bytes) => {
1✔
46
                debug!("Read string: '{}', bytes read: {}", line, bytes);
2✔
47
                bytes as u64
1✔
48
            },
49
            Err(e) => {
×
50
                error!("Reading string line, position: {}, error: {}", current_position, e);
×
51
                0
×
52
            }
53
        };
54
        current_position += bytes_read;
2✔
55
        debug!("End read position: {}\n", current_position);
3✔
56

57
        let line_info = parse_audit_log(line.clone());
2✔
58
        if line_info.contains_key("type") && (line_info["type"] == "SYSCALL" ||
3✔
59
            line_info["type"] == "CWD" ||
2✔
60
            line_info["type"] == "PATH" ||
1✔
61
            line_info["type"] == "PROCTITLE") {
1✔
62
            data.push(line_info.clone());
2✔
63
            if line_info.contains_key("type") &&
1✔
64
                line_info["type"] == "PROCTITLE" {
1✔
65
                debug!("PROCTITLE line detected, breaking loop. Data: {:?}", data);
3✔
66
                break;
67
            }
68
        }
69
        line = String::new();
2✔
70
    }
71
    if !data.is_empty() {
2✔
72
        let last = data.last().unwrap();
2✔
73
        let first = data.first().unwrap();
1✔
74
        if last.contains_key("type") &&
2✔
75
            first.contains_key("type") &&
1✔
76
            last["type"] == "PROCTITLE" &&
1✔
77
            first["type"] == "SYSCALL" {
1✔
78
            let (syscall, cwd, proctitle, paths) = extract_fields(data.clone());
2✔
79
            let audit_vec = cfg.audit.to_vec();
2✔
80

81
            // Skip the event generation of paths not monitored by FIM
82
            if paths.iter().any(|p| {
4✔
83
                let cwd_path = cwd["cwd"].as_str();
1✔
84
                cfg.path_in(p["name"].as_str(), cwd_path, audit_vec.clone()) ||
2✔
85
                cfg.path_in(cwd_path, "", audit_vec.clone())
×
86
            }) {
87
                event = AuditEvent::from(syscall, cwd, proctitle, paths, cfg.clone());
1✔
88
            }
89
        }else if data.iter().any(|line| {
2✔
90
            line["type"] == "SYSCALL" ||
×
91
            line["type"] == "CWD" ||
×
92
            line["type"] == "PATH" ||
×
93
            line["type"] == "PROCTITLE"
×
94
        }) && itx < 120 {
×
95
            current_position = position;
×
96
        }else{
97
            debug!("Audit log discarded, data: {:?}", data);
×
98
        }
99
    }
100
    (event, current_position)
1✔
101
}
102

103
// ----------------------------------------------------------------------------
104

105
pub fn extract_fields(data: Vec<HashMap<String, String>>) -> (SHashMap,
1✔
106
    SHashMap, SHashMap, Vec<SHashMap>) {
107
    let mut paths: Vec<SHashMap> = Vec::new();
1✔
108
    let mut syscall = SHashMap::new();
1✔
109
    let mut cwd = SHashMap::from([ (String::from("cwd"), String::from("/UNKNOWN")) ]);
2✔
110
    let mut proctitle = SHashMap::new();
1✔
111

112
    data.iter().for_each(|v| {
3✔
113
        match v["type"].as_str() {
1✔
114
            "SYSCALL" => syscall.clone_from(v),
2✔
115
            "PATH" => paths.push(v.clone()),
2✔
116
            "CWD" => cwd.clone_from(v),
2✔
117
            "PROCTITLE" => proctitle.clone_from(v),
2✔
118
            _ => error!("Unidentified Audit field")
×
119
        }
120
    });
121
    (syscall, cwd, proctitle, paths)
1✔
122
}
123

124
// ----------------------------------------------------------------------------
125

126
pub fn parse_audit_log(log: String) -> HashMap<String, String> {
1✔
127
    let fields: Vec<&str> = log.split(' ').collect();
2✔
128
    fields.iter()
2✔
129
        .map(|f| {
2✔
130
            let obj: Vec<&str> = f.split('=').collect();
1✔
131
            if obj.len() == 2 {
2✔
132
                (String::from(obj[0]), String::from(obj[1]).replace(['\"', '\n'], ""))
2✔
133
            }else{
134
                (String::from(obj[0]), String::from("UNKNOWN"))
×
135
            }
136
        }).collect::<HashMap<String, String>>()
2✔
137
}
138

139
// ----------------------------------------------------------------------------
140

141
#[cfg(test)]
142
mod tests {
143
    use super::*;
144

145
    #[test]
146
    fn test_read_log() {
147
        if utils::get_os() == "linux" {
148
            let cfg = AppConfig::new("linux", Some("test/system/audit_config.yml"));
149
            let (event, position) = read_log(String::from("test/unit/audit.log"),
150
                cfg, 0, 0);
151
            let audit_event = event.get_audit_event();
152

153
            assert_eq!(audit_event.id.len(), 36);
154
            assert_eq!(audit_event.path, ".");
155
            assert_eq!(audit_event.operation, "CREATE");
156
            assert_eq!(audit_event.file, "sedTsutP7");
157
            assert_eq!(audit_event.timestamp, "1659026449689");
158
            assert_eq!(audit_event.proctitle, "736564002D6900737C68656C6C6F7C4849217C670066696C6531302E747874");
159
            assert_eq!(audit_event.cap_fver, "0");
160
            assert_eq!(audit_event.inode, "1972630");
161
            assert_eq!(audit_event.cap_fp, "0");
162
            assert_eq!(audit_event.cap_fe, "0");
163
            assert_eq!(audit_event.item, "1");
164
            assert_eq!(audit_event.cap_fi, "0");
165
            assert_eq!(audit_event.dev, "08:02");
166
            assert_eq!(audit_event.mode, "0100000");
167
            assert_eq!(audit_event.cap_frootid, "0");
168
            assert_eq!(audit_event.ouid, "0");
169
            assert_eq!(audit_event.paths[0]["item"], "0");
170
            assert_eq!(audit_event.paths[0]["name"], "./");
171
            assert_eq!(audit_event.paths[0]["inode"], "1966138");
172
            assert_eq!(audit_event.paths[0]["dev"], "08:02");
173
            assert_eq!(audit_event.paths[0]["mode"], "040755");
174
            assert_eq!(audit_event.paths[0]["ouid"], "1000");
175
            assert_eq!(audit_event.paths[0]["ogid"], "0");
176
            assert_eq!(audit_event.paths[0]["rdev"], "00:00");
177
            assert_eq!(audit_event.paths[0]["nametype"], "PARENT");
178
            assert_eq!(audit_event.paths[0]["cap_fp"], "0");
179
            assert_eq!(audit_event.paths[0]["cap_fi"], "0");
180
            assert_eq!(audit_event.paths[0]["cap_fe"], "0");
181
            assert_eq!(audit_event.paths[0]["cap_fver"], "0");
182
            assert_eq!(audit_event.paths[0]["cap_frootid"], "0");
183
            assert_eq!(audit_event.paths[1]["item"], "1");
184
            assert_eq!(audit_event.paths[1]["name"], "./sedTsutP7");
185
            assert_eq!(audit_event.paths[1]["inode"], "1972630");
186
            assert_eq!(audit_event.paths[1]["dev"], "08:02");
187
            assert_eq!(audit_event.paths[1]["mode"], "0100000");
188
            assert_eq!(audit_event.paths[1]["ouid"], "0");
189
            assert_eq!(audit_event.paths[1]["ogid"], "0");
190
            assert_eq!(audit_event.paths[1]["rdev"], "00:00");
191
            assert_eq!(audit_event.paths[1]["nametype"], "CREATE");
192
            assert_eq!(audit_event.paths[1]["cap_fp"], "0");
193
            assert_eq!(audit_event.paths[1]["cap_fi"], "0");
194
            assert_eq!(audit_event.paths[1]["cap_fe"], "0");
195
            assert_eq!(audit_event.paths[1]["cap_fver"], "0");
196
            assert_eq!(audit_event.paths[1]["cap_frootid"], "0");
197
            assert_eq!(audit_event.cwd, "/tmp/test");
198
            assert_eq!(audit_event.syscall, "257");
199
            assert_eq!(audit_event.ppid, "161880");
200
            assert_eq!(audit_event.comm, "sed");
201
            assert_eq!(audit_event.fsuid, "0");
202
            assert_eq!(audit_event.pid, "161937");
203
            assert_eq!(audit_event.a0, "ffffff9c");
204
            assert_eq!(audit_event.a1, "556150ee3c00");
205
            assert_eq!(audit_event.a2, "c2");
206
            assert_eq!(audit_event.a3, "180");
207
            assert_eq!(audit_event.arch, "c000003e");
208
            assert_eq!(audit_event.auid, "1000");
209
            assert_eq!(audit_event.items, "2");
210
            assert_eq!(audit_event.gid, "0");
211
            assert_eq!(audit_event.euid, "0");
212
            assert_eq!(audit_event.sgid, "0");
213
            assert_eq!(audit_event.uid, "0");
214
            assert_eq!(audit_event.tty, "pts0");
215
            assert_eq!(audit_event.success, "yes");
216
            assert_eq!(audit_event.exit, "4");
217
            assert_eq!(audit_event.ses, "807");
218
            assert_eq!(audit_event.key, "fim");
219
            assert_eq!(audit_event.suid, "0");
220
            assert_eq!(audit_event.egid, "0");
221
            assert_eq!(audit_event.fsgid, "0");
222
            assert_eq!(audit_event.exe, "/usr/bin/sed");
223
            assert_eq!(position, 850);
224
        }
225
    }
226

227
    // ------------------------------------------------------------------------
228

229
    #[test]
230
    fn test_extract_fields() {
231
        let mut data = Vec::<HashMap<String, String>>::new();
232
        data.push(HashMap::from([ (String::from("type"), String::from("SYSCALL")) ]));
233
        data.push(HashMap::from([ (String::from("type"), String::from("CWD")) ]));
234
        data.push(HashMap::from([ (String::from("type"), String::from("PROCTITLE")) ]));
235
        data.push(HashMap::from([ (String::from("type"), String::from("PATH")),
236
            (String::from("nametype"), String::from("CREATE")) ]));
237
        data.push(HashMap::from([ (String::from("type"), String::from("PATH")),
238
            (String::from("nametype"), String::from("PARENT")) ]));
239
        let (a, b, c, vd) = extract_fields(data);
240
        assert_eq!(a["type"], String::from("SYSCALL"));
241
        assert_eq!(b["type"], String::from("CWD"));
242
        assert_eq!(c["type"], String::from("PROCTITLE"));
243
        assert_eq!(vd[0]["type"], String::from("PATH"));
244
        assert_eq!(vd[0]["nametype"], String::from("CREATE"));
245
        assert_eq!(vd[1]["type"], String::from("PATH"));
246
        assert_eq!(vd[1]["nametype"], String::from("PARENT"));
247
    }
248

249
    // ------------------------------------------------------------------------
250

251
    #[test]
252
    fn test_parse_audit_log() {
253
        let audit_line = String::from("type=CWD msg=audit(1659026449.689:6434): cwd=\"/tmp/test\"");
254
        let map = parse_audit_log(audit_line);
255
        assert_eq!(map["type"], "CWD");
256
        assert_eq!(map["msg"], "audit(1659026449.689:6434):");
257
        assert_eq!(map["cwd"], "/tmp/test");
258
        assert_eq!(map.len(), 3);
259
    }
260
}
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