Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,14 @@ public struct BinaryProtocol {
guard let rawSize = read16() else { return nil }
originalSize = Int(rawSize)
}
guard originalSize >= 0 && originalSize <= FileTransferLimits.maxFramedFileBytes else { return nil }
guard originalSize >= 0 else { return nil }
guard originalSize <= FileTransferLimits.maxFramedFileBytes else {
SecureLogger.warning(
"🚫 Compressed payload expanded size exceeds limit: \(originalSize) bytes > \(FileTransferLimits.maxFramedFileBytes) bytes",
category: .security
)
Comment on lines +371 to +374

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid logging each oversized frame twice

When an oversized compressed frame is padded, decode(_:) first calls decodeCore on the padded data and then retries after MessagePadding.unpad; both attempts reach this warning before returning nil, so one received frame emits two identical security warnings and inflates any diagnostic count. Log the rejection only once across the two decode attempts, or propagate the rejection reason to the outer decoder.

Useful? React with 👍 / 👎.

return nil
}
let compressedSize = payloadLength - lengthFieldBytes
guard compressedSize > 0, let compressed = readData(compressedSize) else { return nil }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,37 @@ struct BinaryProtocolTests {
let encoded = try #require(BinaryProtocol.encode(packet), "Failed to encode oversized packet")
#expect(BinaryProtocol.decode(encoded) == nil)
}

@Test("Log and reject compressed payloads above the expanded-size limit")
func compressedPayloadExpandedSizeAboveLimitIsRejected() {
var malformedData = Data()
malformedData.append(2) // v2
malformedData.append(MessageType.message.rawValue)
malformedData.append(1) // ttl
malformedData.append(contentsOf: Data(repeating: 0, count: 8)) // timestamp
malformedData.append(BinaryProtocol.Flags.isCompressed)

// The v2 payload length includes the four-byte original-size preamble.
let payloadLength: UInt32 = 6
malformedData.append(contentsOf: [
UInt8((payloadLength >> 24) & 0xFF),
UInt8((payloadLength >> 16) & 0xFF),
UInt8((payloadLength >> 8) & 0xFF),
UInt8(payloadLength & 0xFF)
])
malformedData.append(contentsOf: Data(repeating: 0x01, count: BinaryProtocol.senderIDSize))

let expandedSize = UInt32(FileTransferLimits.maxFramedFileBytes + 1)
malformedData.append(contentsOf: [
UInt8((expandedSize >> 24) & 0xFF),
UInt8((expandedSize >> 16) & 0xFF),
UInt8((expandedSize >> 8) & 0xFF),
UInt8(expandedSize & 0xFF)
])
malformedData.append(contentsOf: [0x78, 0x9C]) // compressed bytes are never reached

#expect(BinaryProtocol.decode(malformedData) == nil)
}

// MARK: - Message Padding Tests

Expand Down