ToolUp.ContentScanners.ClamAv 0.22.0

Prefix Reserved
dotnet add package ToolUp.ContentScanners.ClamAv --version 0.22.0
                    
NuGet\Install-Package ToolUp.ContentScanners.ClamAv -Version 0.22.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="ToolUp.ContentScanners.ClamAv" Version="0.22.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="ToolUp.ContentScanners.ClamAv" Version="0.22.0" />
                    
Directory.Packages.props
<PackageReference Include="ToolUp.ContentScanners.ClamAv" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add ToolUp.ContentScanners.ClamAv --version 0.22.0
                    
#r "nuget: ToolUp.ContentScanners.ClamAv, 0.22.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package ToolUp.ContentScanners.ClamAv@0.22.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=ToolUp.ContentScanners.ClamAv&version=0.22.0
                    
Install as a Cake Addin
#tool nuget:?package=ToolUp.ContentScanners.ClamAv&version=0.22.0
                    
Install as a Cake Tool

ToolUp.ContentScanners.ClamAv

IContentScanner over a ClamAV clamd daemon, for deployments that must scan uploaded bytes inside the platform — with the verdict in the platform's own audit trail — rather than bolting scanning on outside and losing the linkage between a refusal and the subject, scope and file that caused it.

Server-only companion. Zero third-party dependencies: clamd's INSTREAM protocol is a command byte-string, length-prefixed chunks and a NUL-terminated reply line, so this package speaks it directly over BCL TcpClient. There is no NuGet client library to track, and nothing vendor-shaped reaches ToolUp.Platform.* (GP 1).

Composing it

open ToolUp.Platform
open ToolUp.Platform.ContentScanners.ClamAv

let scanner = ClamAvContentScanner.create "clamav.internal"

app
|> KnowledgeBase.Server.withContentScanning scanner ContentScanPolicy.failClosed

ContentScanPolicy decides only what a ScanUnavailable verdict means — a daemon that is down, unreachable or that failed mid-stream:

Policy On ScanUnavailable
ContentScanPolicy.failClosed (default) refuse the upload
ContentScanPolicy.failOpen admit the upload

A ScanRejected verdict is always a refusal, and no policy softens it.

Tuning beyond the defaults (port 3310, 64 KiB frames, a 30s ceiling):

let scanner =
    ClamAvOptions.create "clamav.internal"
    |> ClamAvOptions.withPort 3310
    |> ClamAvOptions.withTimeout (System.TimeSpan.FromSeconds 60.0)
    |> ClamAvContentScanner.createWith

Health

ClamAvHealthCheck PINGs the daemon and reports on /ready. Its verdict follows the composed ContentScanPolicy, because an unreachable daemon costs a fail-closed deployment every upload (Unhealthy — take the replica out of rotation) and costs a fail-open deployment only a control (Degraded, with the message saying out loud that uploads are being admitted unscanned).

ServerApp.withHealthCheck (ClamAvHealthCheck(scanner, policy) :> IHealthCheck) app

Verdict mapping

Only an explicit clamd OK is ScanClean; only an explicit FOUND is ScanRejected (the signature name is carried through into the audit row and the refusal message). Everything else — an ERROR reply, a truncated reply, a refused connection, a timeout, StreamMaxLength exceeded — is ScanUnavailable. The honest statement in those cases is "this payload was not scanned", and mapping any of them to ScanClean here would quietly convert every deployment to fail-open regardless of the policy it configured.

Scan never raises: socket failures are reported as ScanUnavailable with the underlying message as the reason.

Operating clamd

Any reachable clamd works — a container, a sidecar, or a host daemon. TCP must be enabled (TCPSocket 3310 in clamd.conf), and StreamMaxLength must be at least as large as the deployment's MaxUploadBytes, or oversized uploads come back ScanUnavailable rather than being scanned.

ClamAV is GPL-licensed, and this companion neither links nor redistributes it — it talks to a daemon the operator runs, over a socket. Nothing ClamAV-licensed ships in this package.

Testing

ToolUp.Platform.Tests carries two arms. The structural arm is always on and covers the reply grammar, the frame encoding, the seam's no-op default and the fail-open/closed policy split — no daemon, no Docker. The live arm is gated on TOOLUP_CLAMAV_HOST (optionally TOOLUP_CLAMAV_PORT) and scans the real EICAR test string through a real daemon; it reports Pending when the variable is unset, so a fresh checkout and CI are green without provisioning anything.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.22.0 99 8/27/2026
0.21.0 93 8/26/2026
0.20.1 99 8/20/2026
0.20.0 100 8/19/2026