Securing File Uploads in Frappe with Virus Scanning using ClamAV and PyClamd
In today's digital world, securing file uploads is not just good practice—it's essential. While Frappe/ERPNext offers a robust platform for web applications and ERP systems, it doesn't scan uploaded files for malware or viruses by default.

To strengthen our application's security, we’ve integrated ClamAV—an open-source antivirus engine with Frappe using the pyclamd Python library. This setup ensures that every uploaded file is scanned before it's saved, helping protect our software and our users.
🔧 Why Virus Scanning?
Allowing users to upload files without checking for malware creates a significant risk. Malicious files can:
- Exploit vulnerabilities in the system.
- Be downloaded by other users, causing data breaches.
- Get synced with other systems, spreading the threat further.
Adding a virus scanning layer ensures only clean files are accepted.
⚙️ Step-by-Step Setup
1. Install Required Packages
source env/bin/activate
pip3 install pyclamd
sudo apt-get install clamav clamav-daemon -y
2. Configure ClamAV Daemon
Edit the ClamAV config file:
sudo nano /etc/clamav/clamd.conf
Uncomment or add the following:
TCPSocket 3310
TCPAddr 127.0.0.1
Then restart the daemon:
sudo systemctl restart clamav-daemon
You can verify the daemon is running:
sudo systemctl status clamav-daemon
sudo lsof -i :3310
⚠️ Note: For development environments, you might use
chmod 666 /var/run/clamav/clamd.ctl
, but this is not recommended for production. Configure proper permissions instead.
🧩 Integrating with Frappe
We use the before_insert hook on the File doctype to scan every file before it's saved.
1. Add Hook in hooks.py
doc_events = {
"File": {
"before_insert": "yourapp.path.to.file.scan_file_for_virus"
}
}
2. Add Virus Scan Code (file.py)
📄 Python Code to Scan Uploaded Files for Viruses
import pyclamd
from frappe import _
import frappe
import os
def scan_file_for_virus(doc, method):
if "private/" in str(doc.file_url):
file_path = frappe.get_site_path(doc.file_url.strip("/"))
else:
file_path = frappe.get_site_path(doc.file_url.strip("/"))
full_path = os.path.abspath(file_path)
cd = pyclamd.ClamdNetworkSocket()
if cd.ping():
result = cd.scan_file(full_path)
if result:
frappe.throw(_("Virus detected in uploaded file! Upload aborted."))
✅ Benefits of This Integration
- Prevents malicious uploads that could compromise your system or your users.
- Works seamlessly with Frappe's file system.
- Easily extendable to scan files in background jobs or workflows.
📬 Let's Connect
Got questions or want help implementing this? Feel free to reach out or drop your thoughts in the comments. Let's build secure, user-friendly software together.
No comments yet. Login to start a new discussion Start a new discussion