Environment
Summary
The receiver reads the machine-provided whole-file MD5 from block zero, but only uses it to decide whether an existing local file can be reused. It does not retain that digest and verify the bytes received during the current transfer. A peer that sends EOT after only a valid prefix therefore gets an ACK and the receiver reports a nonnegative byte count, leaving the caller to treat an incomplete artifact as a successful download.
Root cause
Reproduction
Use a deterministic XMODEM stream containing:
- Block zero advertising the MD5 of the intended complete file.
- Block one containing only a truncated prefix.
- EOT.
On the pinned revision, the receiver ACKs EOT and returns 8192 rather than rejecting the artifact. The regression assertion fails as:
The same pinned revision also fails to preserve the cache shortcut when the equivalent local MD5 is supplied in uppercase; it proceeds with the transfer instead of returning 0, leaving the output empty, and issuing three CAN writes.
Impact
Ordinary TCP packet loss does not manufacture an EOT, so this is specifically an integrity failure when the peer ends the protocol prematurely. The remote play command is sent before the download in Controller.py, so this reproduction does not establish remote mid-cut execution. It does establish that incomplete local downloads can be accepted and then affect supported local behavior such as previewing, line counting, resuming, or reusing the artifact.
Acceptance criteria
- Retain the expected MD5 supplied in block zero.
- Incrementally hash only the actual, unpadded payload bytes written to the destination.
- ACK EOT for protocol compatibility, but return
None when the expected digest is missing or does not match.
- Preserve the block-zero cache shortcut, including case-insensitive comparison of equivalent hexadecimal MD5 strings: return
0, write no output, and send exactly three CAN bytes.
- Add deterministic regression coverage for matching content, mismatched/truncated content, and the uppercase-local-MD5 cache shortcut.
Environment
develop2ea66c10e96d88013a04441134d3d4f8e8c88510Summary
The receiver reads the machine-provided whole-file MD5 from block zero, but only uses it to decide whether an existing local file can be reused. It does not retain that digest and verify the bytes received during the current transfer. A peer that sends EOT after only a valid prefix therefore gets an ACK and the receiver reports a nonnegative byte count, leaving the caller to treat an incomplete artifact as a successful download.
Root cause
download_file()treats every nonnegative result as success and deletes the destination only when the receiver returnsNone.Reproduction
Use a deterministic XMODEM stream containing:
On the pinned revision, the receiver ACKs EOT and returns
8192rather than rejecting the artifact. The regression assertion fails as:The same pinned revision also fails to preserve the cache shortcut when the equivalent local MD5 is supplied in uppercase; it proceeds with the transfer instead of returning
0, leaving the output empty, and issuing three CAN writes.Impact
Ordinary TCP packet loss does not manufacture an EOT, so this is specifically an integrity failure when the peer ends the protocol prematurely. The remote
playcommand is sent before the download inController.py, so this reproduction does not establish remote mid-cut execution. It does establish that incomplete local downloads can be accepted and then affect supported local behavior such as previewing, line counting, resuming, or reusing the artifact.Acceptance criteria
Nonewhen the expected digest is missing or does not match.0, write no output, and send exactly three CAN bytes.