From c7055a9c634b818e9f9be9dd721308e02d1207cc Mon Sep 17 00:00:00 2001 From: Aditya Singh Date: Sun, 24 May 2026 22:25:48 -0700 Subject: [PATCH] Fix incorrect byte shift in IPv4Address byte-array initializer The IPv4Address(_ bytes: [UInt8]) initializer shifted the third octet by 16 bits instead of 8. This OR'd the third octet into the same bit range as the second octet, so the second octet was corrupted and the third was dropped, while bits 8 through 15 were always left zero. For example, decoding [192, 168, 1, 1] produced 192.169.0.1 instead of 192.168.1.1. The error was not caught because the byte property getter (which correctly uses >> 8 for the third octet) had no symmetric test for the initializer, so the two were never checked against each other. The IPv6Address byte-array initializer uses the correct descending shifts. Change the third octet shift to 8 bits so the initializer is the inverse of the bytes property, and add round-trip and invalid-length tests for the byte-array initializer. Signed-off-by: Aditya Singh --- .../ContainerizationExtras/IPv4Address.swift | 2 +- .../TestIPv4Address.swift | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/Sources/ContainerizationExtras/IPv4Address.swift b/Sources/ContainerizationExtras/IPv4Address.swift index 1aea7a01..eb7546b1 100644 --- a/Sources/ContainerizationExtras/IPv4Address.swift +++ b/Sources/ContainerizationExtras/IPv4Address.swift @@ -39,7 +39,7 @@ public struct IPv4Address: Sendable, Hashable, CustomStringConvertible, Equatabl self.value = (UInt32(bytes[0]) << 24) | (UInt32(bytes[1]) << 16) - | (UInt32(bytes[2]) << 16) + | (UInt32(bytes[2]) << 8) | UInt32(bytes[3]) } diff --git a/Tests/ContainerizationExtrasTests/TestIPv4Address.swift b/Tests/ContainerizationExtrasTests/TestIPv4Address.swift index 13fe9432..416f263a 100644 --- a/Tests/ContainerizationExtrasTests/TestIPv4Address.swift +++ b/Tests/ContainerizationExtrasTests/TestIPv4Address.swift @@ -85,6 +85,38 @@ struct IPv4AddressTests { try IPv4Address(invalidAddress) } } + + @Test( + "byte array initializer - valid addresses", + arguments: [ + ([UInt8(127), 0, 0, 1], UInt32(0x7F00_0001)), // localhost + ([UInt8(0), 0, 0, 0], UInt32(0x0000_0000)), // zero address + ([UInt8(255), 255, 255, 255], UInt32(0xFFFF_FFFF)), // broadcast + ([UInt8(192), 168, 1, 1], UInt32(0xC0A8_0101)), // private network + ([UInt8(0x12), 0x34, 0x56, 0x78], UInt32(0x1234_5678)), // byte order test + ] + ) + func testByteArrayInitializerValid(bytes: [UInt8], expectedValue: UInt32) throws { + let address = try IPv4Address(bytes) + #expect(address.value == expectedValue) + // The byte array initializer must round-trip with the bytes property. + #expect(address.bytes == bytes) + } + + @Test( + "byte array initializer - invalid lengths", + arguments: [ + [], // empty + [UInt8(1)], // too short + [UInt8(1), 2, 3], // too short + [UInt8(1), 2, 3, 4, 5], // too long + ] + ) + func testByteArrayInitializerInvalid(bytes: [UInt8]) { + #expect(throws: AddressError.self) { + try IPv4Address(bytes) + } + } } // MARK: - Property Tests