File security: ClamAV and YARA-X before any ingestion
French version: file-security. Decision: ADR-0166. API reference: control-plane-v1.
No file uploaded by a user, an agent or a connector reaches Docling, OCR, media storage, transcription, RAG or a model before it has been scanned by ClamAV (antivirus) and YARA-X (detection rules). Everything is installed and updated locally: no signature and no rule is read from the Internet.
1. Mandatory pipeline
Upload → Quarantine → ClamAV + YARA-X → Validation → Docling → RAG / AI
| Stage | What happens |
|---|---|
| Upload | An ingestion route receives the bytes of the file (base64 or multipart). |
| Quarantine | A scan record is created in the quarantined state before the content is read; a crash leaves it in quarantine, never released. |
| ClamAV + YARA-X | Builtin checks (size, real type read from the bytes, encrypted file, archive, nesting), then both engines, both mandatory. |
| Validation | The verdict and the tenant policy decide: released only when everything is clean, otherwise blocked. A release yields a scan clearance bound to the sha256 of the bytes. |
| Docling | Conversion, OCR, media storage and transcription require this clearance: without it, or for other bytes, they refuse to run. |
| RAG / AI | The extracted text still goes through the existing screening (prompt injection markers, hidden text, RAG quarantine): the antivirus does not protect against prompt injection. |
Routes covered: POST /v1/knowledge-bases/{kb_id}/documents, POST /v1/media, POST /v1/multimodal/vision, POST /v1/multimodal/stt, POST /v1/multimodal/diarize, POST /v1/multimodal/dlp, POST /v1/multimodal/images/edit, POST /v1/multimodal/video/process and POST /v1/gateway/audio/transcriptions. A blocked file gives 422 file_blocked (details.scan_id, details.verdict, details.reasons); no reachable engine gives 503 file_scan_unavailable.
2. Installation by the Navin Manager
Both services are in the Security and policies category of the catalog: navin-manager/catalog/services/clamav.yaml and navin-manager/catalog/services/yara-x.yaml.
| Service | Role | Licence | Modes | Official documentation |
|---|---|---|---|---|
| ClamAV 1.4.6 LTS | Antivirus: viruses, trojans, malicious macros, infected files, archives | GPL-2.0-only (separate process, exception LEX-022) | standalone, replicated (2 hosts or more) | https://docs.clamav.net/ |
| YARA-X 1.22.1 | Detection by rules, signatures and suspicious patterns | BSD-3-Clause (exception LEX-023) | standalone, replicated (2 workers or more) | https://virustotal.github.io/yara-x/ |
On every ClamAV host, clamd only listens on the loopback and a local socket: its protocol has neither TLS nor authentication. An HTTPS gateway authenticated by key only forwards INSTREAM, PING and VERSION. freshclam only reads the internal mirror declared in the mirror_url parameter (https required, no default value). Plan about 3 GB of memory per host for the full database. The YARA-X worker serves its compiled rules over HTTPS, with the same certificate authority as the Manager console.
ClamAV is installed by the podman method with the official image docker.io/clamav/clamav:1.4.6 (and a pinned Python image for the gateway), served by the Manager mirror: the distribution packages do not pin version 1.4.6 and their units point to the public update servers. YARA-X is installed by the python method (wheel yara-x==1.22.1 from the mirror). The offline bundle of the Manager accepts the signature databases (--clamav-databases) and the rules (--yara-rules), checked by verify-bundle.
The navin-enterprise service requires both bindings: the Manager renders for the platform the addresses of every instance, the keys and the certificate authority, and imposes EAI_FILE_SCAN_MODE=enforce.
3. Platform settings
| Setting | Role |
|---|---|
EAI_FILE_SCAN_MODE | enforce (mandatory in production, required by the startup guard) or dev (development: an engine that is not configured is recorded not_configured, a configured engine applies as in enforce). |
EAI_CLAMAV_URLS | ClamAV gateways separated by commas, all https; a load balancer address is accepted too. |
EAI_CLAMAV_API_KEY | Key of the ClamAV gateways. |
EAI_YARAX_URLS | YARA-X workers separated by commas, all https. |
EAI_YARAX_API_KEY | Key of the YARA-X workers. |
EAI_FILE_SCAN_CA_FILE | Certificate authority (PEM) of the gateways and workers. |
EAI_FILE_SCAN_TIMEOUT_SECONDS | Timeout of one scan per engine (60 s by default). |
EAI_FILE_SCAN_MAX_BYTES | Size ceiling of the platform; a tenant policy can only lower it. |
4. Verdicts and blocks
| Verdict | Cause | Result |
|---|---|---|
clean | No engine and no check flags the file (findings of rules in report mode are logged) | released |
malware | ClamAV signature or YARA-X rule with a malware verdict | blocked |
suspicious | Office macro (block_office_macros), PUA, suspicious YARA-X rule | blocked |
unscannable | Encrypted file or archive, scan limits exceeded, broken executable, excessive nesting | blocked |
policy_violation | Size, type not allowed, declared type different from the real type, archive forbidden | blocked |
scan_error | Error or timeout on every instance of an engine | blocked |
Precedence goes from malware to clean in the order of the table. Samples are only kept for blocked files, encrypted at rest, purged after quarantine_retention_days; a released file is never kept by this pipeline.
The navin-baseline YARA-X rules block by default: the EICAR test file, Office documents with a VBA project or an auto-executing macro, PDFs with JavaScript started by an action (fillable PDF forms with formatting scripts included), PDFs with a Launch action or an auto-run embedded file, Windows executables embedded in a document, booby-trapped OLE objects in RTF files, HTML smuggling and scripts that download then run a payload (PowerShell, Windows Script Host, shell). To only log a family of files that is legitimate at the bank, publish in the internal repository a version of the rules where that rule carries action = "report".
ClamAV only reads the start of a ZIP member bigger than MaxFileSize without reporting it: the ClamAV gateway and the platform read the central directory of every ZIP (zip, docx, xlsx, pptx, odt...) before the scan and classify a member or a total beyond the limits (EAI_FILE_SCAN_ARCHIVE_MAX_MEMBER_BYTES, EAI_FILE_SCAN_ARCHIVE_MAX_TOTAL_BYTES) as unscannable, so that no part of a file goes through unscanned.
5. Tenant policy
GET /v1/file-security/policy gives the effective policy; PUT /v1/file-security/policy (security_officer, tenant_admin) changes it and audits the change. Locked rules cannot be changed (422 file_security_rule_locked): scan before any ingestion, both engines mandatory, fail closed, malware and uninspectable files blocked.
{"max_file_bytes": 26214400, "allow_archives": false, "block_office_macros": true,
"block_type_mismatch": true, "retain_samples": true, "quarantine_retention_days": 30,
"max_signature_age_hours": 48, "stale_signatures": "block"}
stale_signatures set to block blocks every scan while the oldest ClamAV database is older than max_signature_age_hours; set to warn, the age is only reported in the status and the alerts.
6. Controlled updates of signatures and rules
From the service sheet in the Navin Manager console:
| Command | Service | Effect |
|---|---|---|
signature_status | clamav | Version and date of the databases installed on each host. |
update_signatures | clamav | freshclam from the internal mirror only, then clamd reload. |
import_signatures | clamav | Offline import of a directory: each database is checked by sigtool before an atomic install, then clamd reloads. |
rules_status | yara-x | Version, sha256 and number of loaded rules. |
update_rules | yara-x | Download from the internal repository (rules_repository_url, https), check of the expected sha256, compilation in a staging area, atomic switch; any failure keeps the active rules. |
rollback_rules | yara-x | Back to the previous version of the rules. |
The internal signature mirror is prepared as described in https://docs.clamav.net/appendix/CvdPrivateMirror.html. GET /v1/file-security/status shows for each instance the signature version, its age and the rules version.
7. Logging and alerts
- Every scan is a record (
GET /v1/file-security/scans,GET /v1/file-security/scans/{scan_id}) with the sha256, the real type, the result of each engine and the reasons, and an audit event. - Every block emits the security event
security.file.blockedand a SOC detection; an engine outage emitssecurity.file.scan_unavailable. - Metrics:
eai_file_scans_total,eai_file_scan_duration_seconds,eai_file_scan_engine_up,eai_file_scan_signature_age_hours,eai_file_scan_engine_errors_total. - Alert rules shipped by the Manager (
navin-manager/catalog/templates/prometheus/rules.yml.j2):NavinFileMalwareBlocked,NavinFileScanEngineDown,NavinFileScanSignaturesStale,NavinFileScanBlockedSpike. GET /v1/file-security/statssummarises a window (24 h by default);POST /v1/file-security/scans/{scan_id}/rescanscans a kept sample again without ever releasing the original upload;DELETE /v1/file-security/scans/{scan_id}/samplepurges the sample.
The File security screen of the portal gathers the pipeline, the engine status, the scans, the blocked files, the policy and the test of a file (POST /v1/file-security/scans).
8. High availability
With two ClamAV gateways and two YARA-X workers or more (replicated mode), the platform client tries each instance in turn and sets a failed instance aside for a short time: losing one host does not block ingestion. Losing every instance of an engine blocks ingestion (503), as intended. Place the instances on distinct hosts and fault domains; the HA checks of the Manager verify it at install time.
9. SDKs
from enterprise_ai import EnterpriseAI
client = EnterpriseAI()
print(client.file_security.status()["ready"])
scan = client.file_security.scan(open("contract.pdf", "rb").read(), filename="contract.pdf")
print(scan["status"], scan["verdict"], scan["reasons"])
The TypeScript (client.fileSecurity), Java (client.fileSecurity()) and Go (client.FileSecurity) SDKs expose the same methods: status, policy, scan, list, detail, rescan, sample purge and statistics.
10. Procedures
The screen is Security and governance > File security (portal/src/screens/file-security.tsx), open to
platform_admin, tenant_admin, security_officer and auditor, with the tabs Engines, Analyses,
Blocked files and Rules.
-
Check that the platform is ready to scan. Uploads are refused while the engines do not answer (fail closed), so check the engines before opening ingestion to users.
Screen: File security > Engines. API:
GET /v1/file-security/status.Expected result: the mode is
enforce, the status is Ready, every ClamAV gateway and YARA-X worker is Up and the signatures are not stale.If it fails: "Not configured" means the engine is not installed or its address is missing (install it with the Navin Manager); "Down" on every instance means ingestion answers 503
file_scan_unavailable; "Stale signatures" means runningupdate_signaturesfrom the service sheet of the Navin Manager. -
Test a file. The file goes through quarantine, ClamAV, YARA-X and the rules like an upload, but it is never ingested.
Screen: File security > Test a file, choose the file, Analyse. API:
POST /v1/file-security/scans.Expected result: the verdict and the reasons; Open the analysis shows the result of each engine.
If it fails: 503
file_scan_unavailable(no engine reachable); a verdict other thancleanis the expected answer for a dangerous file, for example the EICAR test file isblocked. -
Review a blocked file. A blocked file stays in quarantine with its encrypted sample when
retain_samplesis on.Screen: File security > Blocked files, open the analysis, then Rescan or Purge the sample. API:
POST /v1/file-security/scans/{scan_id}/rescan,DELETE /v1/file-security/scans/{scan_id}/sample.Expected result: a rescan creates a new analysis without ever releasing the original upload; a purge deletes the sample for good, the record and its verdict stay.
If it fails: 409
file_sample_unavailablemeans no sample is kept (already purged orretain_samplesoff). -
Change the tenant rules. Only a security officer or a tenant administrator changes them, and every change is audited.
Screen: File security > Rules, change the rules of the tenant, Save the rules. API:
PUT /v1/file-security/policy.Expected result: "Rules saved.";
GET /v1/file-security/policyreturns the new effective policy.If it fails: 422
file_security_rule_lockedmeans a locked rule (scan before ingestion, required engines, fail closed, block malware, block unscannable files); 422invalid_requestnames the field refused (details.field), for example a value outside its bounds or an unknown type.
11. Troubleshooting
| Status | Code | Cause | Fix |
|---|---|---|---|
| 422 | file_blocked | the file was blocked (details.verdict, details.reasons) | read the analysis details.scan_id; a legitimate family is allowed only by a rules version with action = "report" |
| 503 | file_scan_unavailable | no instance of an engine answers | check the engines tab, restart the instances with the Navin Manager |
| 422 | file_security_rule_locked | a locked rule was changed | keep the locked rules, change only the rules of the tenant |
| 409 | file_sample_unavailable | no sample kept for this analysis | upload the file again |
| 403 | tenant_required | a call without a tenant | call with a tenant credential |
| 404 | not_found | unknown analysis | check scan_id and the tenant |
12. Next steps
- Storage administration
- API Reference
- Decision: ADR-0166