fix(network): validate MAC address length when parsing addresses from network manager

This fixes a bug in the HwAddress struct that allowed invalid MAC addresses to pass validation.

---

- [x] I have disclosed use of any AI generated code in my commit
messages.
- If you are using an LLM, and do not fully understand the changes it is
making to the code base, do not create a PR.
- In our experience, AI generated code often results in overly complex
code that lacks enough context for a proper fix or feature inclusion.
This results in considerably longer code reviews. Due to this, AI
authored or partially authored PRs may be closed without comment.
- [x] I understand these changes in full and will be able to respond to
review comments.
- [x] My change is accurately described in the commit message.
- [x] My contribution is tested and working as described.
- [x] I have read the [Developer Certificate of
Origin](https://developercertificate.org/) and certify my contribution
under its conditions.
This commit is contained in:
Anthony Lannutti 2026-06-24 10:10:28 -05:00 committed by GitHub
parent 84729d1dcd
commit 3bef2b08da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -1,38 +1,102 @@
#[derive(Copy, Clone, PartialEq, Eq, Default, Debug, PartialOrd, Ord)]
#[derive(Clone, PartialEq, Eq, Default, Debug, PartialOrd, Ord)]
pub struct HwAddress {
address: u64,
octets: Vec<u8>,
}
impl HwAddress {
pub fn from_str(arg: &str) -> Option<Self> {
let columnless_vec = arg.split(":").collect::<Vec<&str>>();
if columnless_vec.len() * 3 - 1 != arg.len() {
let segments: Vec<&str> = arg.split(":").collect();
// Only accept 6-byte (EUI-48) or 8-byte (EUI-64) addresses
if segments.len() != 6 && segments.len() != 8 {
return None;
}
for byte in &columnless_vec {
if byte.len() != 2 {
let mut octets: Vec<u8> = Vec::new();
for segment in segments {
if segment.len() != 2 {
return None;
}
let byte: u8 = u8::from_str_radix(segment, 16).ok()?;
octets.push(byte);
}
u64::from_str_radix(columnless_vec.join("").as_str(), 16)
.ok()
.map(|address| HwAddress { address })
}
pub fn from_string(arg: &str) -> Option<Self> {
HwAddress::from_str(arg)
Some(HwAddress { octets })
}
}
impl std::fmt::Display for HwAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let hex = format!("{:#x}", self.address)
.trim_start_matches("0x")
.chars()
.collect::<Vec<_>>()
.chunks(2)
.map(|chunk| chunk.iter().cloned().collect::<String>())
.collect::<Vec<String>>()
.join(":");
write!(f, "{}", hex)
let hex_parts: Vec<String> = self
.octets
.iter()
.map(|byte| format!("{:02x}", byte))
.collect();
write!(f, "{}", hex_parts.join(":"))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_valid_6_byte_mac() {
let mac: &str = "00:11:22:33:44:55";
let hw_addr: HwAddress = HwAddress::from_str(mac).expect("should parse valid MAC");
// Access the internal octets field
assert_eq!(hw_addr.octets.len(), 6);
assert_eq!(hw_addr.octets, vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55]);
}
#[test]
fn test_display_6_byte_mac() {
let hw_addr: HwAddress = HwAddress {
octets: vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55],
};
assert_eq!(format!("{}", hw_addr), "00:11:22:33:44:55");
}
#[test]
fn test_parse_valid_8_byte_mac() {
let mac: &str = "00:11:22:33:44:55:66:77";
let hw_addr: HwAddress = HwAddress::from_str(mac).expect("should parse valid EUI-64 MAC");
assert_eq!(hw_addr.octets.len(), 8);
assert_eq!(
hw_addr.octets,
vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77]
);
}
#[test]
fn test_display_8_byte_mac() {
let hw_addr: HwAddress = HwAddress {
octets: vec![0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77],
};
assert_eq!(format!("{}", hw_addr), "00:11:22:33:44:55:66:77");
}
#[test]
fn test_reject_invalid_length_macs() {
let invalid_macs: Vec<(&str, &str)> = vec![
("00", "1-byte MAC"),
("00:11:22:33", "4-byte MAC"),
("00:11:22:33:44", "5-byte MAC"),
("00:11:22:33:44:55:66", "7-byte MAC"),
("00:11:22:33:44:55:66:77:88", "9-byte MAC"),
("00:11:22:33:44:55:66:77:88:99:aa:bb", "12-byte MAC"),
];
for (mac, description) in invalid_macs {
assert!(
HwAddress::from_str(mac).is_none(),
"should reject {}",
description
);
}
}
}