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

Achiefs / fim / 14275166216

04 Apr 2025 10:13PM UTC coverage: 84.711% (-0.4%) from 85.125%
14275166216

push

github

web-flow
Merge pull request #194 from Achiefs/177-db-hash

Added Hash diff scanner

353 of 433 new or added lines in 12 files covered. (81.52%)

4 existing lines in 1 file now uncovered.

1435 of 1694 relevant lines covered (84.71%)

1.52 hits per line

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

83.46
/src/utils.rs
1
// Copyright (C) 2021, Achiefs.
2

3
// To manage unique event identifier
4
use uuid::Uuid;
5
// To get own process ID
6
use std::process;
7
// To get Operating system
8
use std::env;
9
// To use files IO operations.
×
10
use std::fs::{File, metadata};
11
use std::io::{Read, SeekFrom};
12
use std::io::prelude::*;
13
use crate::appconfig;
14
// To manage paths
×
15
use std::path::{Path, PathBuf};
16
// To run commands
17
use std::process::Command;
18
// To log the program process
×
19
use log::{warn, debug, error};
20
// To manage maps
×
21
use std::collections::HashMap;
22
use std::time::{SystemTime, UNIX_EPOCH};
23
use walkdir::WalkDir;
24

NEW
25
#[cfg(test)]
×
26
mod test;
27

UNCOV
28
// ----------------------------------------------------------------------------
×
29

30
/// Function to pop last char of a given String
31
pub fn pop(string: &str) -> &str {
1✔
32
    let mut chars = string.chars();
1✔
33
    chars.next_back();
1✔
34
    chars.as_str()
1✔
35
}
36

UNCOV
37
// ----------------------------------------------------------------------------
×
38

39
pub fn get_hostname() -> String {
1✔
40
    gethostname::gethostname().into_string().unwrap()
1✔
41
}
42

43
// ----------------------------------------------------------------------------
44

45
pub fn get_uuid() -> String {
1✔
46
    format!("{}", Uuid::new_v4())
1✔
47
}
48

UNCOV
49
// ----------------------------------------------------------------------------
×
50

51
pub fn get_pid() -> u32 {
1✔
52
    process::id()
1✔
53
}
54

55
// ----------------------------------------------------------------------------
56

57
pub const fn get_os() -> &'static str {
UNCOV
58
    env::consts::OS
×
59
}
60

61
// ----------------------------------------------------------------------------
62

63
// Function to read file from begin to end
64
pub fn read_file(path: &str) -> String {
1✔
65
    let mut file = File::open(path).unwrap();
1✔
66
    let mut contents = String::new();
1✔
67

68
    file.read_to_string(&mut contents).unwrap();
2✔
69
    contents
1✔
70
}
71

72
// ----------------------------------------------------------------------------
73

74
// (Only supported in Linux) Function to get machine id of the host
75
pub fn get_machine_id() -> String {
1✔
76
    read_file(appconfig::MACHINE_ID_PATH)
1✔
77
}
78

79
// ----------------------------------------------------------------------------
80

81
// Function to get file name of a given path
82
pub fn get_filename_path(path: &str) -> String {
1✔
83
    String::from(
84
        match Path::new(path).file_name() {
2✔
85
            Some(value) => value,
1✔
86
            None => {
87
                debug!("Cannot retrieve event file, event path is empty.");
2✔
88
                std::ffi::OsStr::new(path)
1✔
89
            }
90
        }.to_str().unwrap()
91
    )
92
}
93

94
// ----------------------------------------------------------------------------
95

96
// Function to clean trailing slash of a path
97
pub fn clean_path(path: &str) -> String {
2✔
98
    String::from(if path.ends_with('/') || path.ends_with('\\'){ pop(path) }else{ path })
2✔
99
}
100

101
// ----------------------------------------------------------------------------
102

103
// Function to get the last byte of a given file
104
pub fn get_file_end(file: &str, itx: u64) -> u64 {
1✔
105
    if itx < 5 {
2✔
106
        match File::open(file) {
1✔
107
            Ok(mut d) => d.seek(SeekFrom::End(0)).unwrap(),
1✔
108
            Err(e) => {
1✔
109
                warn!("Cannot open file '{}', due to: {}, retrying...", file, e);
3✔
110
                get_file_end(file, itx + 1)
2✔
111
            }
112
        }
113
    }else{ 0 }
1✔
114
}
115

116
// ----------------------------------------------------------------------------
117

118
// Function to get the last byte of a given file
119
pub fn open_file(file: &str, itx: u64) -> File {
1✔
120
    if itx < 5 {
1✔
121
        match File::open(file) {
1✔
122
            Ok(d) => d,
1✔
123
            Err(e) => {
1✔
124
                warn!("Cannot open file '{}', due to: {}, retrying...", file, e);
3✔
125
                open_file(file, itx + 1)
2✔
126
            }
127
        }
128
    }else{
129
        panic!("Cannot open '{}'", file);
1✔
130
    }
131
}
132

133
// ----------------------------------------------------------------------------
134

135
pub fn check_auditd() -> bool {
×
136
    match Command::new("which")
×
137
        .arg("auditctl")
138
        .output()
139
        .expect("[ERROR] Failed to execute auditctl command check")
140
        .status
141
        .success() {
142
        true => {
143
            debug!("Auditctl command available");
×
144
            true
×
145
        },
146
        _ => {
147
            warn!("Auditctl command unavailable");
×
148
            false
×
149
        }
150
    }
151
}
152

153
// ------------------------------------------------------------------------
154

155
// Returns if raw_path contains compare_path
156
pub fn match_path(raw_path: &str, compare_path: &str) -> bool {
2✔
157
    let max_pops = 128;
2✔
158
    let mut pops = 0;
2✔
159
    let pattern = if get_os() == "linux" { "/" }else{ "\\" };
2✔
160
    let compare_path_clean = &clean_path(compare_path);
2✔
161
    let raw_path_clean = &clean_path(raw_path);
2✔
162

163
    let mut raw_tokens: Vec<&str>;
164
    let mut compare_tokens: Vec<&str>;
165

166
    match metadata(raw_path_clean) {
2✔
167
        Ok(md) => if md.is_file(){
4✔
168
            raw_tokens = raw_path_clean.split(pattern).collect();
2✔
169
            raw_tokens.pop();
1✔
170
        }else{
171
            raw_tokens = raw_path_clean.split(pattern).collect();
4✔
172
        }
173
        ,
174
        Err(e) => { 
1✔
175
            debug!("Cannot fetch metadata information of '{}', assuming not a file. Error: {}", raw_path_clean, e);
3✔
176
            raw_tokens = raw_path_clean.split(pattern).collect();
3✔
177
        }
178
    };
179
    match metadata(compare_path_clean) {
4✔
180
        Ok(md) => if md.is_file(){
4✔
181
            compare_tokens = compare_path_clean.split(pattern).collect();
×
182
            compare_tokens.pop();
×
183
        }else{
184
            compare_tokens = compare_path_clean.split(pattern).collect();
4✔
185
        },
186
        Err(e) => {
1✔
187
            debug!("Cannot fetch metadata information of '{}', assuming not a file. Error: {}", compare_path_clean, e);
3✔
188
            compare_tokens = compare_path_clean.split(pattern).collect();
2✔
189
        }
190
    };
191

192
    while raw_tokens.len() > compare_tokens.len() && pops < max_pops {
6✔
193
        raw_tokens.pop();
1✔
194
        pops += 1;
1✔
195
    }
196
    
197
    raw_tokens.iter().zip(compare_tokens.iter()).all(|(r,c)| {
8✔
198
        clean_path(r) == clean_path(c)
4✔
199
    })
200
}
201

202
// ----------------------------------------------------------------------------
203

204
#[cfg(not(tarpaulin_include))]
205
/// Get the current workdir for FIM, it returns a String with complete path.
206
pub fn get_current_dir() -> String {
207
    String::from(env::current_dir().unwrap_or_else(|_| PathBuf::from(".")).to_str()
208
        .unwrap_or("."))
209
}
210

211
// ----------------------------------------------------------------------------
212

213
pub fn get_field(data: HashMap<String, String>, field_name: &str) -> String {
1✔
214
    let alternative = match field_name {
215
        "nametype" => "objtype",
3✔
216
        _ => field_name
1✔
217
    };
218
    match data.get(field_name) {
1✔
219
        Some(value) => String::from(value),
2✔
220
        None => {
221
            debug!("Cannot not fetch field name trying alternative");
3✔
222
            match data.get(alternative) {
2✔
223
                Some(alt) => String::from(alt),
×
224
                None => {
225
                    debug!("Cannot not fetch alternative. Using default");
3✔
226
                    String::from("UNKNOWN")
2✔
227
                }
228
            }
229
        }
230
    }
231
}
232

233
// ----------------------------------------------------------------------------
234

235
pub fn get_file_size(filename: &str) -> u64 {
1✔
236
    match metadata(filename) {
1✔
237
        Ok(data) => data.len(),
1✔
238
        Err(e) => {
1✔
239
            debug!("Cannot retrieve file size, error: {}", e);
3✔
240
            0
1✔
241
        }
242
    }
243
}
244

245
// ----------------------------------------------------------------------------
246

247
pub fn get_audit_rule_permissions(value: Option<&str>) -> String {
1✔
248
    let mut rule: String = String::new();
1✔
249
    match value {
1✔
250
        Some(value) => {
1✔
251
            for c in value.chars(){
3✔
252
                match c {
1✔
253
                    'r'|'R' => rule.push('r'),
2✔
254
                    'w'|'W' => rule.push('w'),
2✔
255
                    'a'|'A' => rule.push('a'),
2✔
256
                    'x'|'X' => rule.push('x'),
2✔
257
                    _ => rule = String::from("wax")
×
258
                }
259
            }
260
            rule.clone()
2✔
261
        },
262
        None => String::from("wax")
×
263
    }
264
}
265

266
// ----------------------------------------------------------------------------
267

268
pub fn run_auditctl(args: &[&str]) {
1✔
269
    let auditctl_path = if Path::new("/usr/sbin/auditctl").exists() { "/usr/sbin/auditctl"
2✔
270
    } else { "/sbin/auditctl" };
1✔
271

272
    match Command::new(auditctl_path)
2✔
273
    .args(args)
274
    .output()
275
    {
276
        Ok(d) => debug!("Auditctl command info: {:?}", d),
×
277
        Err(e) => error!("Auditctl command error: {}", e)
1✔
278
    };
279
}
280

281
// ----------------------------------------------------------------------------
282

283
pub fn get_current_time_millis() -> String {
1✔
284
    format!("{:?}", SystemTime::now().duration_since(UNIX_EPOCH)
1✔
285
        .expect("Time went backwards").as_millis())
286
}
287

288
// ----------------------------------------------------------------------------
289

290
pub fn get_fs_list(root: String) -> Vec<String> {
1✔
291
    let mut list = Vec::new();
1✔
292
    for result in WalkDir::new(root) {
4✔
293
        list.push(String::from(result.unwrap().path().to_str().unwrap()))
3✔
294
    }
295
    list
1✔
296
}
297

298
// ----------------------------------------------------------------------------
299

300
#[cfg(target_family = "unix")]
301
pub fn get_unix_permissions(file: &str) -> u32 {
1✔
302
    use std::os::unix::fs::PermissionsExt;
303
    let metadata = Path::new(file).metadata().unwrap();
1✔
304
    format!("{:o}", metadata.permissions().mode()).parse::<u32>().unwrap()
3✔
305
}
306

307
// ----------------------------------------------------------------------------
308

309
#[cfg(target_family = "windows")]
310
pub fn get_unix_permissions(_v: &str) -> u32 {
311
    return 0
312
}
313

314
// ----------------------------------------------------------------------------
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

© 2025 Coveralls, Inc