According to the documentation, when you call $transaction->body_filename, you get a temporary file name that points at a file that contains the message. If you examine body_filename, it has no headers.
The clamdscan plugin uses body_filename to hand off to clamdscan. Which means that ClamAV doesn't get to see the headers. Which is important to some ClamAV detections (eg: the ClamAV self-test email is _not_ caught by the clamdscan plugin). [In contrast, the spamassassin plugin carefully spits out header->as_string and then the body into spamd.] Is this working as intended?