enhancement
major
minor
major
minor
major
#29004
Allow to use a virus scanner for uploads
Currently, uploaded files are checked only based on their names (FileNameStrategy, size and name limits). There is no content check—in particular, no virus scan. Operators who are required to scan uploads for regulatory reasons cannot therefore use TopLogic without implementing their own extension.
According to this ticket, an administrator can enable a virus scanner (ClamAV, clamd) for all uploads via the deployment configuration. A file detected as infected is rejected before it reaches a control; the user sees a message displaying the filename and the detected signature. Without activation, nothing changes.
Solution
Extension point. UploadContentChecker (in com.top_logic.basic.io.binary.scan) is the content-based counterpart to FileNameStrategy: it receives the upload as BinaryData and returns a message key if the upload is rejected. The UploadSecurityService (ConfiguredManagedClass) executes the configured checkers in sequence. The ClamAvScanner streams the content to clamd via the INSTREAM protocol(host, port, timeouts, chunk size, and maximum scan size are configurable) and supports an unavailable policy (REJECT = default, fail-closed; ACCEPT = fail-open) if the daemon is unreachable.
Deployment. Activation and configuration are performed via environment variables or system properties (upload_virus_scan_enabled, upload_virus_scan_host, upload_virus_scan_port, upload_virus_scan_unavailable_policy); aliases are defined in tl_basic.xml; no pre-configured settings need to be edited.
Centralized enforcement. The check is not located in the individual upload controls (of which there are eight without a common superclass), but in the TopLogicServlet, the base for all TopLogic servlets, including the ReactServlet. If the service is active and the request is a multipart upload, the HttpServletRequest is wrapped in a Guard. Its ` getParts()`/`getPart()` method causes the container to parse the request on the first call, reads each part exactly once into a TopLogic-specific ` BinaryData ` (temporary file), executes the checkers, and, if a match is found, throws an ` UploadRejectedException ` (an I18N runtime exception with the message key). Otherwise, the Control receives `BinaryDataPart`s: `Part` implementations that supply their content from the ` BinaryData ` and can therefore be read any number of times—regardless of whether the servlet container allows a part to be read multiple times (the servlet specification does not guarantee this). `BinaryDataFactory.createUploadData(Part)` recognizes such a part and returns its data without creating an additional copy. Since the exception is thrown upon the first access to the parts, an upload is always rejected in its entirety; no control processes any part of it.
Notification to the user.
- React (ReactServlet.handleUpload): The rejection is treated the same as exceeding the size limit—a message in the control’s snackbar, HTTP 422 response.
- Classic interface (TopLogicServlet via doService): HTTP 422 response, with the message rendered as an Info Service entry in the body. The upload clients (ajax-form.js for file and image uploads, DragAndDropFile.js for folders and galleries, and the CKEditor plugin imageUploader) display this body in the info area; the drag-and-drop client closes its progress dialog, which previously remained open during every server error.
Without an active service, the request is not wrapped; neither a copy nor runtime costs are incurred.