Skip to content

Commit dcfcf51

Browse files
lfranckeTechassi
andauthored
fix: Code review bug fixes and cleanup (#55)
* fix: Log GID instead of UID in user GID tracing field The tracing statement for `user.gid` was reading from `user.uid` instead of `user.gid`, causing the wrong value to be reported. * refactor: Use idiomatic empty check for disk collection Replace `into_iter().next().is_none()` with `list().is_empty()` for clarity, and use `list().iter()` for the actual collection. * fix: Remove stale .source() call with unused result This was likely a debugging leftover — the error source chain is already captured via the `successors` iterator below. * fix: Fix typo "proess" -> "process" in error message * fix: Replace unwrap() calls with error logging in collection loop JSON serialization and file write can fail at runtime (e.g. disk full). Log the error and continue the loop instead of crashing, since this tool may run continuously for hours. * fix: Use tokio::time::sleep instead of std::thread::sleep std::thread::sleep blocks the entire tokio worker thread. Since main is already async, use the non-blocking alternative. * fix: Handle DNS resolver initialization failure gracefully In a container debugging tool, broken DNS config (/etc/resolv.conf) is a likely scenario to diagnose. Log the error and skip DNS lookups instead of panicking. * fix: Wrap network collector in ComponentResult for consistent error handling The network collector silently swallowed interface listing errors by returning empty data. Now it returns Result so the orchestrator wraps it in ComponentResult, matching the pattern used by other fallible collectors. Errors appear in JSON output instead of being silently lost. * refactor: Use BTreeMap for deterministic JSON key ordering HashMap produces non-deterministic JSON output, making it hard to diff containerdebug output across runs. BTreeMap sorts keys consistently. * fix: Bump rustls-webpki to 0.103.10 to negate RUSTSEC-2026-0049 * chore: Remove undetected, ignored advisories --------- Co-authored-by: Techassi <git@techassi.dev>
1 parent 5332094 commit dcfcf51

File tree

7 files changed

+88
-87
lines changed

7 files changed

+88
-87
lines changed

deny.toml

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,6 @@ targets = [
1414

1515
[advisories]
1616
yanked = "deny"
17-
ignore = [
18-
# https://rustsec.org/advisories/RUSTSEC-2023-0071
19-
# "rsa" crate: Marvin Attack: potential key recovery through timing sidechannel
20-
#
21-
# No patch is yet available, however work is underway to migrate to a fully constant-time implementation
22-
# So we need to accept this, as of SDP 25.3 we are not using the rsa crate to create certificates used in production
23-
# setups.
24-
#
25-
# https://github.com/RustCrypto/RSA/issues/19 is the tracking issue
26-
"RUSTSEC-2023-0071",
27-
28-
# https://rustsec.org/advisories/RUSTSEC-2024-0436
29-
# The "paste" crate is no longer maintained because the owner states that the implementation is
30-
# finished. There are at least two (forked) alternatives which state to be maintained. They'd
31-
# need to be vetted before a potential switch. Additionally, they'd need to be in a maintained
32-
# state for a couple of years to provide any benefit over using "paste".
33-
#
34-
# This crate is only used in a single place in the xtask package inside the declarative
35-
# "write_crd" macro. The impact of vulnerabilities, if any, should be fairly minimal.
36-
#
37-
# See thread: https://users.rust-lang.org/t/paste-alternatives/126787/4
38-
#
39-
# This can only be removed again if we decide to use a different crate.
40-
"RUSTSEC-2024-0436",
41-
]
4217

4318
[bans]
4419
multiple-versions = "allow"

src/error.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ impl<T> ComponentResult<T> {
3131
error = &err as &dyn std::error::Error,
3232
"error reported by {component}, ignoring...",
3333
);
34-
err.source();
3534
ComponentResult::Err {
3635
inner: ComponentError {
3736
message: err.to_string(),

src/main.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,28 @@ async fn main() {
6868
if !next_run_sleep.is_zero() {
6969
tracing::info!(?next_run, "scheduling next run...");
7070
}
71-
std::thread::sleep(next_run_sleep);
71+
tokio::time::sleep(next_run_sleep).await;
7272

7373
let system_information = SystemInformation::collect(&mut collect_ctx).await;
7474

75-
let serialized = serde_json::to_string_pretty(&system_information).unwrap();
76-
if let Some(output_path) = &opts.output {
77-
std::fs::write(output_path, &serialized).unwrap();
75+
match serde_json::to_string_pretty(&system_information) {
76+
Ok(serialized) => {
77+
if let Some(output_path) = &opts.output
78+
&& let Err(err) = std::fs::write(output_path, &serialized)
79+
{
80+
tracing::error!(
81+
path = %output_path.display(),
82+
error = &err as &dyn std::error::Error,
83+
"failed to write JSON output file"
84+
);
85+
}
86+
}
87+
Err(err) => {
88+
tracing::error!(
89+
error = &err as &dyn std::error::Error,
90+
"failed to serialize system information"
91+
);
92+
}
7893
}
7994

8095
match opts.loop_interval {

src/system_information/disk.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ impl Disk {
1212
#[tracing::instrument(name = "Disk::collect_all")]
1313
pub fn collect_all() -> Vec<Self> {
1414
let disks = sysinfo::Disks::new_with_refreshed_list();
15-
if disks.into_iter().next().is_none() {
15+
if disks.list().is_empty() {
1616
tracing::info!("no disks found");
1717
}
18-
disks.into_iter().map(Self::from).collect()
18+
disks.list().iter().map(Self::from).collect()
1919
}
2020
}
2121

src/system_information/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub struct SystemInformation {
1515
pub os: Option<os::OperatingSystem>,
1616
pub current_user: Option<ComponentResult<user::User>>,
1717
pub disks: Option<Vec<disk::Disk>>,
18-
pub network: Option<network::SystemNetworkInfo>,
18+
pub network: Option<ComponentResult<network::SystemNetworkInfo>>,
1919
// TODO:
2020
// Current time
2121
// SElinux/AppArmor
@@ -70,7 +70,10 @@ impl SystemInformation {
7070
user::User::collect_current(&ctx.system),
7171
)),
7272
disks: Some(disk::Disk::collect_all()),
73-
network: Some(network::SystemNetworkInfo::collect().await),
73+
network: Some(ComponentResult::report_from_result(
74+
"SystemNetworkInfo::collect",
75+
network::SystemNetworkInfo::collect().await,
76+
)),
7477
// ..Default::default()
7578
};
7679

src/system_information/network.rs

Lines changed: 60 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -3,73 +3,86 @@ use hickory_resolver::{
33
};
44
use local_ip_address::list_afinet_netifas;
55
use serde::Serialize;
6+
use snafu::{ResultExt, Snafu};
67
use std::{
7-
collections::{BTreeSet, HashMap},
8+
collections::{BTreeMap, BTreeSet},
89
net::IpAddr,
910
sync::LazyLock,
1011
time::Duration,
1112
};
1213
use tokio::task::JoinSet;
1314

14-
static GLOBAL_DNS_RESOLVER: LazyLock<TokioResolver> = LazyLock::new(|| {
15-
let (resolver_config, mut resolver_opts) =
16-
read_system_conf().expect("failed to read system resolv config");
15+
#[derive(Debug, Snafu)]
16+
pub enum Error {
17+
#[snafu(display("failed to list network interfaces"))]
18+
ListInterfaces { source: local_ip_address::Error },
19+
}
20+
21+
static GLOBAL_DNS_RESOLVER: LazyLock<Option<TokioResolver>> = LazyLock::new(|| {
22+
let (resolver_config, mut resolver_opts) = match read_system_conf() {
23+
Ok(conf) => conf,
24+
Err(err) => {
25+
tracing::error!(
26+
error = &err as &dyn std::error::Error,
27+
"failed to read system DNS config, DNS lookups will be skipped"
28+
);
29+
return None;
30+
}
31+
};
1732
resolver_opts.timeout = Duration::from_secs(5);
1833

19-
TokioResolver::builder_with_config(resolver_config, TokioConnectionProvider::default())
20-
.with_options(resolver_opts)
21-
.build()
34+
Some(
35+
TokioResolver::builder_with_config(resolver_config, TokioConnectionProvider::default())
36+
.with_options(resolver_opts)
37+
.build(),
38+
)
2239
});
2340

2441
/// Captures all system network information, including network interfaces,
2542
/// and the results of reverse and forward DNS lookups.
2643
#[derive(Debug, Serialize)]
2744
pub struct SystemNetworkInfo {
28-
pub interfaces: HashMap<String, Vec<IpAddr>>,
29-
pub reverse_lookups: HashMap<IpAddr, Vec<String>>,
30-
pub forward_lookups: HashMap<String, Vec<IpAddr>>,
45+
pub interfaces: BTreeMap<String, Vec<IpAddr>>,
46+
pub reverse_lookups: BTreeMap<IpAddr, Vec<String>>,
47+
pub forward_lookups: BTreeMap<String, Vec<IpAddr>>,
3148
}
3249

3350
impl SystemNetworkInfo {
3451
#[tracing::instrument(name = "SystemNetworkInfo::collect")]
35-
pub async fn collect() -> SystemNetworkInfo {
36-
let interfaces = match list_afinet_netifas() {
37-
Ok(netifs) => {
38-
let mut interface_map = std::collections::HashMap::new();
52+
pub async fn collect() -> Result<SystemNetworkInfo, Error> {
53+
let netifs = list_afinet_netifas().context(ListInterfacesSnafu)?;
54+
let mut interfaces = BTreeMap::new();
3955

40-
// Iterate over the network interfaces and group them by name
41-
// An interface may appear multiple times if it has multiple IP addresses (e.g. IPv4 and IPv6)
42-
for (name, ip_addr) in netifs {
43-
tracing::info!(
44-
network.interface.name = name,
45-
network.interface.address = %ip_addr,
46-
"found network interface"
47-
);
48-
interface_map
49-
.entry(name)
50-
.or_insert_with(Vec::new)
51-
.push(ip_addr);
52-
}
53-
interface_map
54-
}
55-
Err(error) => {
56-
tracing::error!(
57-
error = &error as &dyn std::error::Error,
58-
"failed to list network interfaces"
59-
);
60-
HashMap::new()
61-
}
62-
};
56+
// Iterate over the network interfaces and group them by name
57+
// An interface may appear multiple times if it has multiple IP addresses (e.g. IPv4 and IPv6)
58+
for (name, ip_addr) in netifs {
59+
tracing::info!(
60+
network.interface.name = name,
61+
network.interface.address = %ip_addr,
62+
"found network interface"
63+
);
64+
interfaces
65+
.entry(name)
66+
.or_insert_with(Vec::new)
67+
.push(ip_addr);
68+
}
6369

6470
let ips: BTreeSet<IpAddr> = interfaces.values().flatten().copied().collect();
6571
tracing::info!(network.addresses.ip = ?ips, "ip addresses");
6672

67-
let mut reverse_lookups = JoinSet::new();
73+
let Some(resolver) = GLOBAL_DNS_RESOLVER.as_ref() else {
74+
return Ok(SystemNetworkInfo {
75+
interfaces,
76+
reverse_lookups: BTreeMap::new(),
77+
forward_lookups: BTreeMap::new(),
78+
});
79+
};
80+
81+
let mut reverse_lookup_tasks = JoinSet::new();
6882
for ip in ips {
69-
reverse_lookups
70-
.spawn(async move { (ip, GLOBAL_DNS_RESOLVER.reverse_lookup(ip).await) });
83+
reverse_lookup_tasks.spawn(async move { (ip, resolver.reverse_lookup(ip).await) });
7184
}
72-
let reverse_lookups: HashMap<IpAddr, Vec<String>> = reverse_lookups
85+
let reverse_lookups: BTreeMap<IpAddr, Vec<String>> = reverse_lookup_tasks
7386
.join_all()
7487
.await
7588
.into_iter()
@@ -96,16 +109,12 @@ impl SystemNetworkInfo {
96109
let hostname_set: BTreeSet<String> = reverse_lookups.values().flatten().cloned().collect();
97110
tracing::info!(network.addresses.hostname = ?hostname_set, "hostnames");
98111

99-
let mut forward_lookups = JoinSet::new();
112+
let mut forward_lookup_tasks = JoinSet::new();
100113
for hostname in hostname_set {
101-
forward_lookups.spawn(async move {
102-
(
103-
hostname.clone(),
104-
GLOBAL_DNS_RESOLVER.lookup_ip(hostname).await,
105-
)
106-
});
114+
forward_lookup_tasks
115+
.spawn(async move { (hostname.clone(), resolver.lookup_ip(hostname).await) });
107116
}
108-
let forward_lookups: HashMap<String, Vec<IpAddr>> = forward_lookups
117+
let forward_lookups: BTreeMap<String, Vec<IpAddr>> = forward_lookup_tasks
109118
.join_all()
110119
.await
111120
.into_iter()
@@ -126,10 +135,10 @@ impl SystemNetworkInfo {
126135
})
127136
.collect();
128137

129-
SystemNetworkInfo {
138+
Ok(SystemNetworkInfo {
130139
interfaces,
131140
reverse_lookups,
132141
forward_lookups,
133-
}
142+
})
134143
}
135144
}

src/system_information/user.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::error::SysinfoError;
88
pub enum Error {
99
#[snafu(display("failed to get pid of the current process"))]
1010
GetCurrentPid { source: SysinfoError },
11-
#[snafu(display("current pid {pid} could not be resolved to a proess"))]
11+
#[snafu(display("current pid {pid} could not be resolved to a process"))]
1212
ResolveCurrentProcess { pid: Pid },
1313
}
1414
type Result<T, E = Error> = std::result::Result<T, E>;
@@ -53,7 +53,7 @@ impl User {
5353
tracing::info!(
5454
user.name,
5555
user.uid = user.uid.as_ref().map(|uid| format!("{uid:?}")),
56-
user.gid = user.uid.as_ref().map(|gid| format!("{gid:?}")),
56+
user.gid = user.gid.as_ref().map(|gid| format!("{gid:?}")),
5757
"current user"
5858
);
5959
Ok(user)

0 commit comments

Comments
 (0)